AtCoder Regular Contest 116 总结

A

判断奇偶因子谁多谁少

容易发现如果质因数分解,有 n 个 2 和 m 个非 2 质因子

则奇数因子有 $2^m$ 个 ,偶数因子有 $2^{n+m-1}$ 个(没有 2 时没有)

可知当 n=0 时奇数多, n=1 时一样, n=2 时偶数多

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
int t;
ll n;

int main() {
    for (scanf("%d",&t);t;t--) {
        scanf("%lld",&n);
        if (n%2!=0) {printf("Odd\n");continue;}
        n/=2;
        if (n%2!=0) {printf("Same\n");continue;}
        else printf("Even\n");
    }
}
View Code

B

求对于一个序列 A ,它的子序列 B 的 $\sum_B max(B)*min(B)$

将 A 排序,枚举左右端点为 l,r ,则当前答案为 $a[l]*a[r]*2^{r-l-1} (r>l)$ 再加上 $a[l]*a[r] (r=l)$

考虑用后缀和维护 $a[r]*2^{r-1}$ 则枚举左端点 l 时答案为 $a[l]*s[r]*2^{-l} (r>l)$ 再加上 $a[l]*a[r] (r=l)$

时间复杂度 O(n)

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll P=998244353;
const int N=2e5+10;
int n;
ll a[N],_2[N],suf[N],ans;

ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;}

int main() {
    scanf("%d",&n);
    _2[0]=1;for (int i=1;i<=n;i++) scanf("%lld",&a[i]),_2[i]=(_2[i-1]<<1)%P;
    sort(a+1,a+n+1);
    for (int i=n;i;i--)    suf[i]=(suf[i+1]+a[i]*_2[i-1]%P)%P;
    for (int i=1;i<=n;i++)
        (ans+=a[i]*suf[i+1]%P*Pow(_2[i],P-2)%P+a[i]*a[i]%P)%=P;
    printf("%lld\n",ans);
}
View Code

C

在一个序列中不同的数至多有 $log_2m$ 个

则设 f[i][j] 表示第 i 个不同的数是 j , 从上一个 j 的因子里转移过来,复杂度是调和级数 O(nlnn)

最后枚举有 i 个不同的数,在 n-1 个空隙里插 i-1 个板即可

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const ll P=998244353;
const int N=2e5+10;
int n,m,lg;
ll fact[N],inv[N],f[21][N],ans;

ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;}

ll C(int n,int m) {return fact[n]*inv[n-m]%P*inv[m]%P;}

int main() {
    scanf("%d%d",&n,&m);lg=log2(m);
    fact[0]=1;for (int i=1;i<N;i++) fact[i]=fact[i-1]*i%P;
    inv[N-1]=Pow(fact[N-1],P-2);for (int i=N-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%P;
    for (int i=1;i<=m;i++) f[0][i]=1;
    for (int i=1;i<=lg;i++)
        for (int j=1;j<=m;j++)
            for (int k=2;k*j<=m;k++) (f[i][j*k]+=f[i-1][j])%=P;
    for (int i=0;i<=min(lg,n-1);i++)
        for (int j=1;j<=m;j++) (ans+=f[i][j]*C(n-1,i))%=P;
    printf("%lld\n",ans);
}
View Code

D

范围很小,做法很暴力

从低位到高位,设 f[i][j] 表示做到第 i 位,前面 i-1 位满足相消为 0 的方案数

O(mlogm)

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const ll P=998244353;
const int N=5e3+10;
int n,m,lg;
ll fact[N],inv[N],f[15][N];

ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;}

ll C(int n,int m) {return fact[n]*inv[n-m]%P*inv[m]%P;}

int main() {
    scanf("%d%d",&n,&m);lg=log2(m);
    if (m&1) return printf("0"),0;
    fact[0]=1;for (int i=1;i<=n;i++) fact[i]=fact[i-1]*i%P;
    inv[n]=Pow(fact[n],P-2);for (int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%P;
    f[0][0]=1;
    for (int i=0,k=1;i<=lg;i++,k<<=1)
        for (int j=0;j<=m;j++) {
            for (int l=2;j+k*l<=m&&l<=n;l+=2) (f[i+1][j+k*l]+=f[i][j]*C(n,l)%P)%=P;
            (f[i+1][j]+=f[i][j])%=P;
        }
    printf("%lld\n",f[lg+1][m]);
}
View Code
posted @ 2021-03-29 21:34  Vagari  阅读(61)  评论(0编辑  收藏  举报