CF585E Present for Vitalik the Philatelist

cf

注意一堆数\(\gcd\)的贡献可以改为一堆数公因数的贡献,但是只取最大的

所以我们先对于每个数,把每个出现次数\(>1\)的质因子给除成只有一次,也就是把每个数改成他所有不同质因子的积方便统计答案.然后设\(h_i\)\(i\)是这\(n\)个数里面多少个数的因数,这个可以初始先对每个\(i\)\(h_{a_i}\)加一,然后(以质因子出现次数为维度)高维后缀和求出.这时候再设\(g_i=2^{h_i}-1\)表示集合所有数的\(\gcd\)\(i\)倍数的集合数,以及\(f_i\)为表示集合所有数的\(\gcd\)\(i\)的集合数,可以发现\(g\)\(f\)的后缀和形式,所以可以高维后缀差分求出\(f\)

现在枚举数\(x\)和表示集合所有数\(\gcd\)\(i\),满足\(\gcd(x,i)\neq 1\)就可以加入答案.考虑优化枚举\(x\),对于一个\(i\)\(\gcd(x,i)\neq1\)\(x\)个数.如果我们用\(h\)来统计,对于一个\(i\),假设某个\(x\)和他的\(\gcd\)\(y(y>1)\),那么\(y\)的因数处也会有来自\(x\)的贡献,但是我们只能统计\(x\)\(y\)的贡献,所以考虑容斥,一项一项推可以发现\(y\)项系数为\(-\mu(y)\),所以给所有\(h_y\)乘上对应\(-\mu(y)\)后,高维前缀和即可得到要求的贡献数组

#include<bits/stdc++.h>
#define LL long long
#define db double

using namespace std;
const int N=1e7+10,M=5e5+10,mod=1e9+7;
int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
    return x*w;
}
void ad(int &x,int y){x+=y,x-=x>=mod?mod:0;}
int n,pw[M],a[M],pp[N],prm[N/10],tt,f[N],g[N],nt[N],hd,tl,vs[N],ti;
bool v[N];
void cal(int *f,int fx,int d)
{
    for(int i=1;i<=tt;++i)
    {
	    int x=prm[i],lm=tl/x;
	    ++ti;
    	for(int j=hd,k;j<=lm;j=nt[j+1])
    	{
    	    if(vs[j]==ti) continue;
    	    k=j*x,vs[k]=ti;
    	    if(fx) ad(f[j],(~d)?f[k]:mod-f[k]);
    	    else ad(f[k],(~d)?f[j]:mod-f[j]);
    	}
    }
}

int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    hd=1,tl=1,nt[1]=pp[1]=1;
    for(int i=2;i<=N-5;++i)
    {
    	if(!pp[i]) pp[i]=i,prm[++tt]=i;
    	for(int j=1;i*prm[j]<=N-5;++j)
    	{
    	    pp[i*prm[j]]=prm[j],v[i*prm[j]]|=v[i];
    	    if(i%prm[j]==0) {v[i*prm[j]]=1;break;}
    	}
    	if(!v[i])
    	{
    	    nt[i]=i;
    	    for(int j=tl+1;j<i;++j) nt[j]=i;
    	    tl=i;
    	}
    }
    nt[tl+1]=tl+2;
    n=rd(),pw[0]=1;
    for(int i=1;i<=n;++i) pw[i]=(pw[i-1]<<1)%mod;
    for(int i=0;i<=n;++i) ad(pw[i],mod-1);
    for(int i=1;i<=n;++i)
    {
    	a[i]=rd();
    	int ls=1,sx=1,x=a[i];
    	while(x>1)
    	{
    	    int np=pp[x];
    	    if(np!=ls) sx*=np;
    	    x/=np,ls=np;
    	}
    	a[i]=sx,++f[a[i]];
    }
    cal(f,1,1);
    g[1]=-1;
    for(int i=hd;i<=tl;i=nt[i+1]) g[i]=g[i/pp[i]]+1;
    for(int i=hd;i<=tl;i=nt[i+1])
	g[i]=((g[i]&1)?f[i]:mod-f[i])%mod,f[i]=pw[f[i]];
    cal(f,1,-1),f[1]=0;
    g[1]=0,cal(g,0,1);
    int ans=0;
    for(int i=hd+1;i<=tl;i=nt[i+1]) ad(ans,1ll*f[i]*(n-g[i])%mod);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/smyjr/p/12585089.html