zoukankan      html  css  js  c++  java
  • 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) eq 1)就可以加入答案.考虑优化枚举(x),对于一个(i)(gcd(x,i) eq1)(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
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    Ubuntu 16 安装redis客户端
    crontab 参数详解
    PHP模拟登录发送闪存
    Nginx配置端口访问的网站
    Linux 增加对外开放的端口
    Linux 实用指令之查看端口开启情况
    无敌的极路由
    不同的域名可以指向同一个项目
    MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error
    Redis 创建多个端口
  • 原文地址:https://www.cnblogs.com/smyjr/p/12585089.html
Copyright © 2011-2022 走看看