hdu5321 beautiful set(莫比乌斯反演)

\(cnt[i]\)为权值为i的倍数的数的数量。
\(f0[i],f1[i]\)分别为两种方法\(gcd=i\)的贡献是i的多少倍。
\(F0[i],F1[i]\)分别为两种方法\(gcd\)\(i\)的倍数的贡献是i的多少倍。
\(F0[i]=\sum_{j=1}^{cnt[i]}A_{cnt[i]}^{cnt[i]-j}*(n-j)!*(n-j+1)\)
\(F1[i]=\sum_{j=1}^{cnt[i]}j*C_{cnt[i]}^{j}\)
然后显然有\(F[i]=\sum_{d\mid i}f[d]\)
然后莫比乌斯反演一下
\[f(n)=∑_{n\mid d}μ(\frac{d}{n})F(d)\]
复杂度调和级数\(O(nlnn)\)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int N=101000;
const int p=258280327;
bool book[N];
int prime[N],mu[N],fac[N],inv[N],num;
int a[N],F0[N],f0[N],F1[N],f1[N],mx,cnt[N],T,n;
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
void init(){
    for(int i=0;i<=100000;i++)
        cnt[i]=a[i]=f1[i]=f0[i]=F1[i]=F0[i]=0;
}
int ksm(int x,int b){
    int tmp=1;
    while(b){
        if(b&1)tmp=tmp*x%p;
        x=x*x%p;
        b>>=1;
    }
    return tmp;
}
int A(int n,int m){
    return fac[n]*inv[n-m]%p;
}
int C(int n,int m){
    return fac[n]*inv[n-m]%p*inv[m]%p;
}
void pre_work(){
    mu[1]=1;
    for(int i=2;i<=100000;i++){
        if(book[i]==0){
            prime[++num]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=num&&prime[j]*i<=100000;j++){
            book[i*prime[j]]=1;
            if(i%prime[j]==0)break;
            mu[prime[j]*i]=-mu[i];
        }
    }
    fac[0]=1;
    for(int i=1;i<=100000;i++)fac[i]=(fac[i-1]*i)%p;
    inv[100000]=ksm(fac[100000],p-2);
    for(int i=99999;i>=0;i--)inv[i]=inv[i+1]*(i+1)%p;
}
signed main(){
    pre_work();
    while(scanf("%lld",&n)!=EOF){
        init();
        for(int i=1;i<=n;i++)a[read()]++;
        for(int i=1;i<=100000;i++)
            for(int j=i;j<=100000;j+=i)cnt[i]+=a[j];
        for(int i=1;i<=100000;i++)
            for(int j=1;j<=cnt[i];j++)
                F0[i]=(F0[i]+A(cnt[i],j)*fac[n-j+1])%p,
                F1[i]=(F1[i]+C(cnt[i],j)*j)%p;
        for(int i=1;i<=100000;i++)
            for(int j=i;j<=100000;j+=i)
                f0[i]=(f0[i]+mu[j/i]*F0[j])%p,
                f1[i]=(f1[i]+mu[j/i]*F1[j])%p;
        int ans1=0,ans2=0;
        for(int i=1;i<=100000;i++)
            ans1=(ans1+f0[i]*i)%p,
            ans2=(ans2+f1[i]*i)%p;
        if(ans1>ans2)printf("Mr. Zstu %lld\n",ans1);
        else if(ans1<ans2)printf("Mr. Hdu %lld\n",ans2);
        else printf("Equal %lld\n",ans2);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Xu-daxia/p/10253399.html