标签:name 方案 open const continue com show space div
好题!学习了好多
写法①:
先求出gcd不为1的集合的数量,显然我们可以从大到小枚举计算每种gcd的方案(其实也是容斥),或者可以直接枚举gcd然后容斥(比如最大值是6就用2^cnt[2]-1+3^cnt[3]-1-(6^cnt[6]-1),cnt[x]表示x的倍数的个数),用容斥计算的话可以发现系数是莫比乌斯函数的相反数,就可以线性筛了。下面会记录一种O(MAX*ln(MAX))的筛法...求cnt的话可以选择直接枚举倍数计算O(MAX*ln(MAX))或者分解质因数,因为1e7内最多有8个不同质因子,求出所有质因子再枚举子集给cnt加上贡献。
然后枚举每一个数计算它对答案的贡献,就是gcd不为1的集合的数量里去掉这个数的因数的贡献,分解质因数之后一个一个去掉即可。
这种写法就不写代码啦~(其实写了但是写炸了懒得调了
O(MAX*ln(MAX))莫比乌斯函数筛:
miu[1]=1; for(int i=2;i<=mx;i++) for(int j=i<<1;j<=mx;j+=i) miu[j]-=miu[i];
写法②:
这是一种更简单的写法,尝试把上面统计数量后计算每个数的贡献这两个过程合二为一。
考虑去掉一个数的因数对答案的贡献怎么更简单地做,可以发现一个数的所有因数的cnt里都有这个数的贡献,所以我们实际上可以在统计答案的时候直接计算所有数的贡献顺便去掉这个数的贡献,也就是以下的公式:
中间部分是gcd为x的时候的方案数,右边是gcd为x时对此式子有贡献的数的数量。
我们求莫比乌斯函数的时候用上上面的O(MAX*ln(MAX))版筛法就可以把代码长度做到极短了。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #define MOD(x) ((x)>=mod?(x)-mod:(x)) using namespace std; const int maxn=500010, maxm=1e7+10, inf=1e9+1, mod=1e9+7; int n, x, mx, ans; int mi[maxn], cnt[maxm], miu[maxm]; void read(int &k) { int f=1; k=0; char c=getchar(); while(c<‘0‘ || c>‘9‘) c==‘-‘ && (f=-1), c=getchar(); while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar(); k*=f; } int main() { read(n); mi[0]=1; for(int i=1;i<=n;i++) mi[i]=(1ll*mi[i-1]<<1)%mod; for(int i=1;i<=n;i++) read(x), cnt[x]++, mx=max(mx, x); miu[1]=1; for(int i=1;i<=mx;i++) { for(int j=i<<1;j<=mx;j+=i) miu[j]-=miu[i], cnt[i]+=cnt[j]; if(!cnt[i] || !miu[i]) continue; int delta=1ll*miu[i]*(mi[cnt[i]]-1)%mod*(cnt[i]-n)%mod; delta=MOD(delta+mod); ans=MOD(ans+delta); } printf("%d\n", ans); }
写法③:
是在写法②的基础上的,可以省去求莫比乌斯函数的过程。
对于第一层都是质数的容斥,可以倒着枚举数i,一般可以较为简单的计算i的倍数的总贡献,然后删去i的倍数(此时不包括i)的贡献(此时倍数的贡献已经计算好了)来得到i的贡献。注意!!!这里i的贡献与写法2中容斥式子x=i时的值并不一样!!!这两者完全就不是一个东西!!!这种写法求出的i的贡献并不能直接理解(在求这题的值时没什么意义,求gcd的时候是有gcd为i的集合数的意义的),但是所有数的贡献和求出来就是答案的值,这只是一种比较好写的容斥写法而已。
#include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #define MOD(x) (x>=mod?(x)-mod:(x)) using namespace std; const int maxn=500010, inf=1e9, maxm=1e7+10, mod=1e9+7; int n, x, mx, anss; int ans[maxm], mi[maxn], cnt[maxm]; inline void read(int &k) { int f=1; k=0; char c=getchar(); while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar(); while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar(); k*=f; } int main() { read(n); for(int i=1;i<=n;i++) read(x), cnt[x]++, mx=max(mx, x); mi[0]=1; for(int i=1;i<=n;i++) mi[i]=(1ll*mi[i-1]<<1)%mod; for(int i=mx;i>=2;i--) { int sum=cnt[i]; for(int j=i<<1;j<=mx;j+=i) sum+=cnt[j], ans[i]=MOD(ans[i]-ans[j]+mod); ans[i]+=1ll*(mi[sum]-1)*(n-sum)%mod; ans[i]=MOD(ans[i]); anss=MOD(anss+ans[i]); } printf("%d\n", anss); }
Codeforces 585E. Present for Vitalik the Philatelist(容斥)
标签:name 方案 open const continue com show space div
原文地址:http://www.cnblogs.com/Sakits/p/7965740.html