CF585E Present for Vitalik the Philatelist

比较套路的约数容斥,但是好像还有复杂度更优的做法吧

首先求\(\gcd(S)\ne 1\and \gcd(S,x)=1\)可以转化为\(\gcd(S,x)=1\)的个数减去\(\gcd(S)=1\)的个数

套路地考虑约数容斥,即设\(f_i\)表示\(i|\gcd(S,x)\)的个数,设\(g_i\)表示\(i|\gcd(S)\)的个数

考虑统计出\(c_i\)表示\(i\)的倍数的个数,考虑计算\(f_i\)\(g_i\)

\[f_i=c_i\times (2^{c_i-1}-1)\\g_i=\sum_{j=1}^{c_i} C_{c_i}^j\times (n-j)\\=n\times \sum_{j=1}^{c_i} C_{c_i}^j-\sum_{j=1}^{c_i}C_{c_i}^j\times j\\=n\times (2^{c_i}-1)-c_i\times \sum_{j=1}^{c_i} \frac{(c_i-1)!}{(j-1)!(c_i-j)!}\\=n\times (2^{c_i}-1)-c_i\times 2^{c_i-1} \]

然后直接从大到小枚举每个\(i\),设\(f'_i\)表示\(\gcd(S,x)=i\)的个数,\(f'_i=f_i-\sum_{i|j} f'_j\),对于\(g\)同理

最后的答案就是\(f'_1-g'_1\),总复杂度\(O(|a_i|\ln |a_i|)\)

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int S=1e7+5,mod=1e9+7;
int n,x,c[S],f[S],g[S],mx;
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
	for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline int sub(CI x,CI y)
{
	return x-y<0?x-y+mod:x-y;
}
int main()
{
	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i)
	scanf("%d",&x),mx=max(mx,x),++c[x];
	for (i=1;i<=mx;++i) for (j=i<<1;j<=mx;j+=i)	c[i]+=c[j];
	for (i=1;i<=mx;++i) if (c[i]) f[i]=1LL*c[i]*sub(quick_pow(2,c[i]-1),1)%mod;
	for (i=1;i<=mx;++i) if (c[i]) g[i]=sub(1LL*n*sub(quick_pow(2,c[i]),1)%mod,1LL*c[i]*quick_pow(2,c[i]-1)%mod);
	for (i=mx;i;--i) for (j=i<<1;j<=mx;j+=i) f[i]=sub(f[i],f[j]),g[i]=sub(g[i],g[j]);
	return printf("%d",sub(f[1],g[1])),0;
}
posted @ 2020-12-01 08:32  空気力学の詩  阅读(93)  评论(0编辑  收藏  举报