【2020杭电多校round6】HDU6833 A Very Easy Math Problem

题目大意

题目链接

给定\(k,x\)\(t\)次询问,每次给出一个\(n\)。请你求:

\[\sum_{a_1=1}^{n}\sum_{a_2=1}^{n}\ldots \sum_{a_x=1}^{n}\left (\prod_{j=1}^{x}a_j^k\right )f(\gcd(a_1,a_2,\ldots ,a_x))\cdot \gcd(a_1,a_2,\ldots ,a_x) \]

\(\gcd(a_1,a_2,\ldots ,a_n)\)\(a_1,a_2,...,a_n\)的最大公约数。

函数\(f(x)\)定义为:如果存在一个整数\(k\) (\(k>1\))使得\(k^2\)\(x\)的约数,则\(f(x)=0\),否则\(f(x)=1\)

答案对\(10^9+7\)取模。

数据范围:\(1\leq k,x\leq 10^9\)\(1\leq t\leq 10^4\)\(1\leq n\leq 2\times 10^5\)

本题题解

先考虑单组数据的情况。

可以计算每个\(\gcd\)值的贡献。不过精确考虑“\(\gcd\)等于”几有些困难,我们可以先对每个\(d\)求出“\(\gcd\)\(d\)的倍数”的贡献,再容斥回去:

// F[d] 表示gcd是d的倍数,对答案的贡献
// 容斥成 G[d] 表示gcd就是d,对答案的贡献
int ans=0;
for(int d=n;d>=1;--d){
	G[d]=F[d];
	for(int j=d+d;j<=n;j+=d){
		G[d]-=G[j];
	}
	ans+=G[d]*f[d]*d;
}

考虑钦定了\(\gcd\)\(d\)的倍数,如何求出\(\sum_{a_1=1}^{n}\sum_{a_2=1}^{n}\ldots \sum_{a_x=1}^{n}\left (\prod_{j=1}^{x}a_j^k\right )\),也就是代码片段里的F[d]。此时的限制,就是所有\(a_i\)都要是\(d\)的倍数。可以先令所有\(a_i\)都除以\(d\),那剩下部分的取值,就只有\([1,\lfloor\frac{n}{d}\rfloor]\),每个\(a_i\)都能取这些值,那它们的乘积之和,就等于:\((1^k+2^k+\dots+\lfloor\frac{n}{d}\rfloor^k)^x\)。再把所有的\(d\)乘回去,则\(F[d]=d^{kx}(1^k+2^k+\dots+\lfloor\frac{n}{d}\rfloor^k)^x\)。求出F[]后,再用上面的代码容斥求出答案。

但这只是单组数据时的做法。

对多组数据,每次独立求答案是不现实的。我们考虑从\(n-1\)\(n\)递推答案,每次只需要计算这两者之间微小的变化。

具体来说,要使\(n\)对新答案有影响,则\(d\)一定要是\(n\)的约数(否则\(\lfloor\frac{n}{d}\rfloor=\lfloor\frac{n-1}{d}\rfloor\),求出来的\(F\)值显然还是一样的)。那么我们每次枚举\(n\)的约数,时间复杂度相当于\(1\dots n\)的约数数量之和,约等于\(O(n\log n)\)(可以考虑每个数能作为哪些数的约数,显然是\(i,2i,3i\dots ,\lfloor\frac{n}{i}\rfloor\cdot i\),所以数量是\(\frac{n}{i}\)\(1\dots n\)的总数就是\(\sum_{i=1}^{n}\frac{n}{i}\),也就是调和级数)。

现在,我们虽然能快速求出哪些\(F\)有变化,也能得到新的\(F\)值,但如果每次重新做一遍容斥,复杂度还是无法接受的。我们考虑把这个容斥转化一下,改成求出每个\(F[d]\)在容斥里的贡献系数\(c[d]\)。这样不管\(F[d]\)怎么变化,我们只需要往答案里加上变化值\(\times c[d]\)即可。

\(c[d]\)怎么求呢?可以先初始化,令\(c[d]=f(d)\cdot d\)(这里的\(f\)是题面中定义的函数,千万不要与我们定义的大\(F\)搞混)。然后:

for(int i=1;i<=n;++i)
	c[i]=f[i]*i;
for(int i=1;i<=n;++i){
	for(int j=i+i;j<=n;j+=i){
		c[j]-=c[i];
	}
}

这是因为每个\(F\)值会在它的约数处被减掉。可以把这个代码对照上面的容斥理解,上面的容斥(求出的ans)就等价于\(\sum_{i=1}^{n}F[i]\cdot c[i]\)。这就是我们求出这个系数\(c\)数组的妙处。

于是我们就能\(O(1)\)修改并更新答案了。

总时间复杂度\(O(n\log n+t)\)

参考代码:

//problem:
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

template<typename T>inline void ckmax(T& x,T y){x=(y>x?y:x);}
template<typename T>inline void ckmin(T& x,T y){x=(y<x?y:x);}

const int MOD=1e9+7;
inline int mod1(int x){return x<MOD?x:x-MOD;}
inline int mod2(int x){return x<0?x+MOD:x;}
inline void add(int& x,int y){x=mod1(x+y);}
inline void sub(int& x,int y){x=mod2(x-y);}
inline int pow_mod(int x,int i){int y=1;while(i){if(i&1)y=(ll)y*x%MOD;x=(ll)x*x%MOD;i>>=1;}return y;}

const int MAXN=2e5;

int K,X,n;
int pw[MAXN+5],sum[MAXN+5],pwX_of_pwK[MAXN+5],pwX_of_sum[MAXN+5];
int F[MAXN+5],coef[MAXN+5],ans[MAXN+5];
vector<int>factors[MAXN+5];

int p[MAXN+5],cnt_p,f[MAXN+5];
bool v[MAXN+5];
void sieve(int lim){
	f[1]=1;
	for(int i=2;i<=lim;++i){
		if(!v[i]){
			p[++cnt_p]=i;
			f[i]=1;
		}
		for(int j=1;j<=cnt_p && i*p[j]<=lim;++j){
			v[i*p[j]]=1;
			if(i%p[j]==0){
				break;
			}
			if(f[i])
				f[i*p[j]]=1;
		}
	}
}
void init(){
	sieve(MAXN);
	for(int i=1;i<=MAXN;++i){
		pw[i]=pow_mod(i,K);
		sum[i]=mod1(pw[i]+sum[i-1]);
		pwX_of_pwK[i]=pow_mod(pw[i],X);
		pwX_of_sum[i]=pow_mod(sum[i],X);
	}
}

int main() {
	int T;cin>>T>>K>>X;
	init();
	/*
	//单组数据的简单容斥:
	cin>>n;
	for(int g=1;g<=n;++g){
		F[g]=(ll)pow_mod(sum[n/g],X)*pow_mod(pw[g],X)%MOD;
	}
	int ans=0;
	for(int i=n;i>=1;--i){
		G[i]=F[i];
		for(int j=i+i;j<=n;j+=i){
			sub(G[i],G[j]);
		}
		ans=((ll)ans + (ll)G[i]*f[i]%MOD*i)%MOD;
	}
	cout<<ans<<endl;
	*/
	for(int i=1;i<=MAXN;++i)
		coef[i]=f[i]*i;
	for(int i=1;i<=MAXN;++i){
		for(int j=i+i;j<=MAXN;j+=i){
			sub(coef[j],coef[i]);
		}
	}
	
	ans[1]=F[1]=1;
	for(int i=1;i<=MAXN;++i){
		for(int j=i;j<=MAXN;j+=i){
			factors[j].pb(i);
		}
	}
	for(int i=2;i<=MAXN;++i){
		ans[i]=ans[i-1];
		for(int j=0;j<SZ(factors[i]);++j){
			int g=factors[i][j];
			sub(ans[i],(ll)F[g]*coef[g]%MOD);
			F[g]=(ll)pwX_of_sum[i/g]*pwX_of_pwK[g]%MOD;
			add(ans[i],(ll)F[g]*coef[g]%MOD);
		}
	}
	while(T--){
		cin>>n;
		cout<<ans[n]<<endl;
	}
	return 0;
}
posted @ 2020-08-06 23:49  duyiblue  阅读(267)  评论(1编辑  收藏  举报