正方形 square.cpp

【一句話題意】有t個詢問,求Πi=1nΠj=1nijgcd(i,j)2\Pi^n_{i=1}\Pi^n_{j=1}\frac{i*j}{gcd(i,j)^2}
t<=1e6,n<=1e7 時限比1s長一點。

【分析】
原題不如概括後簡潔和清晰,但概括和抽象也是圖論和數論所要強調的能力。這次考試犯了數論題的大忌:死盯着題目想結論,而不是概括出式子進行觀察和思考

明顯的離線算法,所以問題在於預處理的複雜度上,只能比線性多一點。
將式子展開可以得到n!2nΠi=1nΠj=1ngcd(i,j)\frac{n!^{2n}}{\Pi^n_{i=1}\Pi^n_{j=1}gcd(i,j)}
對於分子可以O(n)預處理,關鍵在於gcd(i,j)的計算。首先考慮的是利用已有結果推得未知結果。

如果我已經知道了f(n-1)。f(n)=f(n1)+n!nnΠi=1ngcd(i,n)f(n)=f(n-1)+\frac{n!*n^n}{\Pi^n_{i=1}gcd(i,n)}顯然gcd(i,n)一定是n的約數,考慮到n的約數個數是log級的,所以我們枚舉gcd的大小。當gcd大小爲x時,顯然有gcd(ix,nx)==1gcd(\frac{i}{x},\frac{n}{x})==1,所以gcd爲x對答案貢獻就是x乘上與nx\frac{n}{x}互質的數的個數。用公式書寫就是xφ(nx)x*\varphi(\frac{n}{x})。考慮到φ(nx)\varphi(\frac{n}{x})可以O(n)預處理,所以複雜度就是O(nlogn)。

還是過不了,怎麼辦?我們單獨考慮每個素數對答案的貢獻,對於一個素數x,它在k*x處會對答案有x(2k-1)的貢獻,求一遍前綴積即爲答案。
【code】

#pragma GCC optimize(2)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int mod=19260817;
const int maxn=1e7+1000;
const int T=1e7;
int sum[maxn],mul[maxn];
bool flag[maxn];
int n,t;
int pw(int x,int p){
	int ret=1;
	while(p>0){
		if(p&1) ret=1ll*ret*x%mod;
		x=1ll*x*x%mod,p>>=1;
	}
	return ret;
}
void pre(){
	flag[1]=true;
	for(int i=0;i<=T;i++) sum[i]=1;
	for(int i=2;i<=T;i++)
		if(flag[i]==false){
			long long t=i;
			for(;t<=T;t*=i){//p^k
				for(int tmp=t,num=i;tmp<=T;tmp+=t,num=1ll*num*i%mod*i%mod)//num的奇形怪狀處理是爲了避免p^k被,p,p^2……處理多次
					sum[tmp]=1ll*sum[tmp]*num%mod;
			}
			for(int k=i;k<=T;k+=i) flag[k]=true;
		}
	for(int i=1;i<=T;i++) sum[i]=1ll*sum[i-1]*sum[i]%mod;
	for(int i=1;i<=T;i++) sum[i]=1ll*sum[i]*sum[i]%mod;
	mul[0]=1;for(int i=1;i<=T;i++) mul[i]=1ll*mul[i-1]*i%mod;
}
inline void read(int &x){
	x=0;char tmp=getchar();
	while(tmp<'0'||tmp>'9') tmp=getchar();
	while(tmp>='0'&&tmp<='9') x=(x<<1)+(x<<3)+tmp-'0',tmp=getchar();
}
int main(){
	pre();
	cin>>t;
	while(t--){
		read(n);
		printf("%lld\n",1ll*pw(mul[n],2*n)*pw(sum[n],mod-2)%mod);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章