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<<maxx<<endl;
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<bits/stdc++.h>
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<<maxx<<endl;
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;
}