17AHU排位赛1 E题(gcd>1序列,容斥)

problem

对于一个长度为n的数组,我们可以随意取出它的一个子序列(子序列可以不连续),如果某个子序列中所有数的最大公约数>1,那么我们就称这个子序列是rbb序列。现在你需要回答长度为n的数组有多少个子序列是rbb序列。最终结果对1e9+7取模

Input

第一行两个整数n(1<=n<=200000)。
第二行n个整数ai(1<=ai<=1000000)

Output

输出该数组rbb序列的个数,结果对1e9+7取模。

Sample Input

3
3 1 3

Sample Output

3

Hint

对于{a1},gcd=3>1,是个rbb序列
对于{a3},gcd=3>1,是个rbb序列
对于{a1,a3},gcd=3>1,是个rbb序列
其它的子序列都不是rbb序列


思路

首先如果一个序列如果都是另一个数的倍数的话,那这个序列就是rbb序列(gcd>1)。对于本题,先利用一个统计每个数出现的次数(顺便记下最大的数maxx)。然后从1~maxx枚举每个数的倍数有多少个(利用桶)。
由在序列中一个数的倍数有多少个(比如x个)就可以得到有2^x-1个不同组合。

问题是,会有重复计算,比如一个序列是4 8 24 它们是2的倍数,是rbb序列;又是4的倍数,也是rbb序列。因此重复计算了,所以要减去。即是2的倍数的序列数要减去4,6,8……的

核心代码

long long ans=0;
    //cout<
    for(int i=maxx;i>=2;--i){
        if(!c[i]) continue;
        f[i]=(b[c[i]]-1)%mod;
        for(int j=i+i;j<=maxx;j+=i) f[i]=(f[i]-f[j]+mod)%mod;
        ans=(ans+f[i])%mod;
    }


代码示例

#include
using namespace std;

const int maxn=1e6+50;
const int mod=1e9+7;

int a[maxn],c[maxn];
//a是桶,c是根据桶a而记录的一个数的倍数有多少
long long b[maxn],f[maxn];//b存2的幂次 辅助
//f是每个倍数序列的子序列数

int n,maxx;

void init_b()
{
    b[0]=1;
    for(int i=1;i<=maxn/2;++i)
        b[i]=(b[i-1]<<1)%mod;
}

int main()
{
    //ios::sync_with_stdio(false);
    init_b();
    maxx=0;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        int x;
        scanf("%d",&x);
        a[x]++;
        maxx=max(maxx,x);
    }
    for(int i=1;i<=maxx;++i)
        for(int j=i;j<=maxx;j+=i)
            c[i]+=a[j];

    long long ans=0;
    //cout<
    for(int i=maxx;i>=2;--i){
        if(!c[i]) continue;
        f[i]=(b[c[i]]-1)%mod;
        for(int j=i+i;j<=maxx;j+=i) f[i]=(f[i]-f[j]+mod)%mod;
        ans=(ans+f[i])%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(-----------,数,学,-----------,---------,解,题,报,告,---------)