【Codeforces】 CF1436F Sum Over Subsets

题目链接

CF方向
Luogu方向

题目解法

首先考虑消去 g c d gcd gcd 的限制
考虑莫比乌斯反演
优先枚举 d d d 可得答案为 ∑ d = 1 n μ ( d ) ∗ a n s ( d ) \sum_{d=1}^{n}\mu(d)*ans(d) d=1nμ(d)ans(d)
其中 a n s ( d ) ans(d) ans(d) 是所有 a i a_i ai d d d 的倍数组成的答案

a i a_i ai d d d 的倍数的所有数的可重集为 S S S
考虑 ∑ x ∈ A x ∗ ∑ y ∈ B y = ∑ x ∈ A ∑ y ∈ B x y \sum_{x\in A}x*\sum_{y\in B}y=\sum_{x\in A}\sum_{y\in B} xy xAxyBy=xAyBxy
考虑每一对 x , y x,y x,y 贡献的答案

  1. x x x y y y 是同一个数(这里的同一个数不是数值相同)
    答案为 x 2 ∗ ( ∣ S ∣ − 1 ) ∗ 2 ∣ S ∣ − 2 x^2*(|S|-1)*2^{|S|-2} x2(S1)2S2
    对于唯一不在 A A A 中,却在 B B B 中的数有 ∣ S ∣ − 1 |S|-1 S1 中选择,其他 ∣ S ∣ − 2 |S|-2 S2 可选可不选
  2. x x x y y y 不是同一个数(可能数值相同)
    答案为 x y ∗ ( ( ∣ S ∣ − 2 ) ∗ 2 ∣ S ∣ − 3 + 2 ∣ S ∣ − 2 ) xy*((|S|-2)*2^{|S|-3}+2^{|S|-2}) xy((S2)2S3+2S2)
    x x x 不是唯一 … 的数,那么唯一 … 的数有 ∣ S ∣ − 2 |S|-2 S2 中选择,其他 ∣ S ∣ − 3 |S|-3 S3 可选可不选
    x x x 是唯一 … 的数,那么其他 ∣ S ∣ − 2 |S|-2 S2 个数可选可不选

考虑 f r e q freq freq 的值域很大,考虑对每个数值共同考虑
分类仍然相同

  1. x x x y y y 是同一个数
    A n s = f r e q x ∗ x 2 ∗ ( ∣ S ∣ − 1 ) ∗ 2 ∣ S ∣ − 2 Ans=freq_x*x^2*(|S|-1)*2^{|S|-2} Ans=freqxx2(S1)2S2
  2. x x x y y y 不是同一个数(可能数值相同)
    继续分类讨论
    x , y x,y x,y 数值相同
    A n s = f r e q x ∗ ( f r e q x − 1 ) ∗ x 2 ∗ ( ( ∣ S ∣ − 2 ) ∗ 2 ∣ S ∣ − 3 + 2 ∣ S ∣ − 2 ) Ans=freq_x*(freq_x-1)*x^2*((|S|-2)*2^{|S|-3}+2^{|S|-2}) Ans=freqx(freqx1)x2((S2)2S3+2S2)
    x , y x,y x,y 数值不同
    s u m = ∑ d ∣ i i ∗ f r e q i sum=\sum_{d|i} i*freq_i sum=diifreqi
    A n s = f r e q x ∗ x ∗ ( s u m − f r e q x ∗ x ) ∗ ( ( ∣ S ∣ − 2 ) ∗ 2 ∣ S ∣ − 3 + 2 ∣ S ∣ − 2 ) Ans=freq_x*x*(sum-freq_x*x)*((|S|-2)*2^{|S|-3}+2^{|S|-2}) Ans=freqxx(sumfreqxx)((S2)2S3+2S2)

实际复杂度为调和级数 O ( n l n    n ) O(nln\;n) O(nlnn)

#include 
#define int long long
using namespace std;
const int N(100100),P(998244353);
int n,ans,mu[N],sum[N];
int pr[N],cnt;
bool vis[N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
int qmi(int a,int b){
	if(b<0) return 0;
	int res=1;
	for(;b;b>>=1){
		if(b&1) res=res*a%P;
		a=a*a%P;
	}
	return res;
}
int solve(int d){
	int tot1=0,tot2=0;
	for(int i=d;i<=100000;i+=d) tot1+=sum[i],tot2=(tot2+sum[i]%P*i%P)%P;
	if(tot1<2) return 0;
	int pw1=qmi(2,(tot1-3)%(P-1)),pw2=qmi(2,(tot1-2)%(P-1));
	tot1%=P;
	int res=0;
	for(int i=d;i<=100000;i+=d){
		int t=sum[i]%P;
		//x,y是同一个数
		res=(res+t*i%P*i%P*(tot1-1+P)%P*pw2%P)%P;
		//x,y的数值相同
		if(sum[i]>=2) res=(res+t*(t-1+P)%P*i%P*i%P*((tot1-2+P)*pw1%P+pw2)%P)%P;
		//x,y数值不同
		res=(res+t*i%P*(tot2-i*t%P+P)%P*((tot1-2+P)*pw1%P+pw2)%P)%P; 
	}
	return res;
}
void sieve(){
	mu[1]=1;
	for(int i=2;i<=100000;i++){
		if(!vis[i]) vis[i]=1,pr[++cnt]=i,mu[i]=-1;
		for(int j=1;j<=cnt&&pr[j]<=100000/i;j++){
			vis[pr[j]*i]=1;
			if(i%pr[j]==0) break;
			mu[i*pr[j]]=-mu[i];
		}
	}
}
signed main(){
	n=read(),sieve();
	for(int i=1,x,y;i<=n;i++) x=read(),y=read(),sum[x]+=y;
	for(int i=1;i<=100000;i++) if(mu[i]) ans=((ans+mu[i]*solve(i))%P+P)%P;
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(Codeforces,算法)