CF585E Present for Vitalik the Philatelist

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

首先求(gcd(S) e 1and 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 imes (2^{c_i-1}-1)\g_i=sum_{j=1}^{c_i} C_{c_i}^j imes (n-j)\=n imes sum_{j=1}^{c_i} C_{c_i}^j-sum_{j=1}^{c_i}C_{c_i}^j imes j\=n imes (2^{c_i}-1)-c_i imes sum_{j=1}^{c_i} frac{(c_i-1)!}{(j-1)!(c_i-j)!}\=n imes (2^{c_i}-1)-c_i imes 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;
}
原文地址:https://www.cnblogs.com/cjjsb/p/14066089.html