1. 程式人生 > 其它 >CF585E Present for Vitalik the Philatelist - 數論,容斥

CF585E Present for Vitalik the Philatelist - 數論,容斥

題解

\(2900\) 就這?

可以發現要求的無非是:

\[\begin{aligned} 2\sum_{A\subset S} [\gcd\{A\}=1]\lvert A\rvert-n\sum_{A\subset S} [\gcd\{A\}=1] \end{aligned} \]

考慮一個常見套路:對於數集 \(A\),限定 \(B\subset A\land \gcd\{B\}=k\) 是不好做的,但 \(k\mid\gcd\{B\}\) 是好做的:其充要條件就是 \(\forall x\in B,k\mid x\)。於是對於每個 \(k\),先統計出來這樣的答案 \(f_k\),然後倒序列舉 \(k\)

,令 \(f_k\gets f_k-\sum_{k\mid i} f_i\) 即可。這個可以用狄利克雷字首和做到 \(\mathcal{O}(V\log\log V)\)

然後就做完了,整體複雜度是 \(\mathcal{O}(n+V\log\log V)\),程式碼寫的是 \(\mathcal{O}(n+V\log V)\) 做法,懶得改了。

程式碼

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
template<typename T> void Read(T &_x){
	_x=0;int _f=1;
	char ch=getchar();
	while(!isdigit(ch)) _f=(ch=='-'?-1:_f),ch=getchar();
	while(isdigit(ch)) _x=_x*10+(ch^48),ch=getchar();
	_x*=_f;
}
template<typename T,typename... Args> void Read(T &_x,Args& ...others){
	Read(_x);Read(others...);
}
typedef long long ll;
const int N=5e5+5,V=1e7+5,Mod=1e9+7;
int n,a[N];
int prime[V/10],prCnt=0;
bool vis[V];
void Sieve(int mx){
	vis[1]=1;
	For(i,2,mx){
		if(!vis[i]) prime[++prCnt]=i;
		for(int j=1;j<=prCnt&&i*prime[j]<=mx;++j){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0) break;
		}
	}
}
void Dirichlet(int mx,ll *arr,int inv){
	For(i,1,prCnt){
		for(int j=1;prime[i]*j<=mx;++j){
			arr[j]=(arr[j]+Mod+1LL*inv*arr[prime[i]*j])%Mod;
		}
	}
}
int cnt[V];
ll pow2[N],f[V],g[V];
int main(){
	Read(n);
	pow2[0]=1;
	For(i,1,n) pow2[i]=pow2[i-1]*2%Mod;
	int mx=0;
	For(i,1,n) Read(a[i]),mx=max(mx,a[i]),++cnt[a[i]];
	Sieve(mx);
	For(i,1,mx){
		int cur=0;
		for(int j=1;j*i<=mx;++j) cur+=cnt[i*j];
		if(cur) f[i]=cur*pow2[cur-1]%Mod,g[i]=(pow2[cur]-1+Mod)%Mod;
	}
	Dirichlet(mx,f,-1);
	Dirichlet(mx,g,-1);
	ll ans=f[1]*2%Mod-g[1]*n%Mod;
	printf("%lld\n",(ans+Mod)%Mod);
	return 0;
}
Written by Alan_Zhao