莫比烏斯反演-數字表格題解

題目

題目鏈接

分析

初步變形

這道題其實是求:

i=1nj=1mfibgcd(i,j)\prod_{i=1}^{n}\prod_{j=1}^{m}{fib_{gcd(i,j)}}

和別的莫比烏斯反演題目不同之處在於:它是累乘不是累加

但是轉念一想,底數累乘實際上就是指數的累加,所以我們可以這樣做:

=d=1nfibddindjm[gcd(i,j)=d]=\prod_{d=1}^{n}{fib_d^{\sum_{d|i}^{n}\sum_{d|j}^{m}[gcd(i,j)=d]}}

然後對於指數,我們把他拿下來單獨處理:之後就都是套路了

dindjm[gcd(i,j)=d]\sum_{d|i}^{n}\sum_{d|j}^{m}[gcd(i,j)=d]

=i=1n/dj=1m/d[gcd(i,j)=1]=\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}{[gcd(i,j)=1]}

=i=1n/dj=1m/dkgcd(i,j)μ(k)=\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}\sum_{k|gcd(i,j)}{μ(k)}

=k=1n/dμ(k)(n/kd)(m/kd)=\sum_{k=1}^{n/d}{μ(k)}*{(n/kd)}*{(m/kd)}

於是答案就是:

=d=1nfibdk=1n/dμ(k)(n/kd)(m/kd)=\prod_{d=1}^{n}{fib_d^{\sum_{k=1}^{n/d}{μ(k)}*{(n/kd)}*{(m/kd)}}}

這個便可以預處理莫比烏斯函數然後用整出分塊做

但是這個的複雜度是o(nn)o(n\sqrt{n})還是過不了

第二步變形

這就涉及另一個套路了:

T=kdT=kd

=d=1nfibdk=1n/dμ(k)(n/kd)(m/kd)=\prod_{d=1}^{n}{fib_d^{\sum_{k=1}^{n/d}{μ(k)}*{(n/kd)}*{(m/kd)}}}

=T=1ndT(fibdμ(T/d))(n/T)(m/T)=\prod_{T=1}^{n}\prod_{d|T}{(fib_d}^{μ(T/d)})^{(n/T)*(m/T)}

其中指數的一部分可以用整出分塊,把另一部分拿出來單獨討論:

dT(fibdμ(T/d))\prod_{d|T}{(fib_d}^{μ(T/d)})

這是一個關於T的函數,而且可以預處理。

預處理方法就是:枚舉d把fibdμ(T/d)fib_d^{μ(T/d)}加到d的倍數T裏面

不明白的看函數裏面init

代碼

下附AC代碼

#include<bits/stdc++.h>
using namespace std;
int read(){
	char s;
	int x=0,f=1;
	while(s<'0'||s>'9'){
		if(s=='-')f=-1;
		s=getchar();
	}
	while(s>='0'&&s<='9'){
		x*=10;
		x+=s-'0';
		s=getchar();
	}
	return x*f;
}
const long long mod=1e9+7;
const long long N=1e6+5;
long long qpow(long long a,long long b){
	if(b==0)return (long long )1;
	long long rec=qpow(a,b/2)%mod;
	if(b&1)return rec*rec%mod*a%mod;
	return rec*rec%mod;
}
long long fib[N];//斐波那契數列
long long g[N];//fib的逆元 
bool flag[N];
int p[N],pn;
long long mu[N];
long long F[N];//F[n] = Π_d|n  fib(d)^mu(T/d)  
void init(int n){
	mu[1]=1;
	fib[1]=1;
	g[1]=1;
	F[0]=F[1]=1;
	for(int i=2;i<=n;i++){
		fib[i]=(fib[i-1]+fib[i-2])%mod;
		g[i]=qpow(fib[i],mod-2)%mod;
		F[i]=1;
		if(!flag[i]){
			p[pn++]=i;
			mu[i]=-1;
		}
		for(int j=0;j<pn,p[j]*i<=n;j++){
			flag[p[j]*i]=1;
			if(i%p[j]==0)break;
			else mu[i*p[j]]=-mu[i];
		}
	}
	for(int i=1;i<=n;i++){
		if(!mu[i])continue;
		for(int j=i;j<=n;j+=i){
			if(mu[i]==1){
				F[j]*=fib[j/i];
				F[j]%=mod;
			} 
			else{
				F[j]*=g[j/i];
				F[j]%=mod;
			}
		}
	}
	for(int i=2;i<=n;i++){
		F[i]=F[i-1]*F[i]%mod;
	}
}
int main(){
	int T=read();
	init(N-5);
	while(T--){
		int n,m;
		n=read(),m=read();
		if(n>m)swap(n,m);
		long long ans=1;
		for(int l=1,r;l<=n;l=r+1){
			r=min(n/(n/l),m/(m/l));
			long long inv=qpow(F[l-1],mod-2);
			ans*=qpow((F[r]*inv%mod),(long long)(n/l)*(m/l)%(mod-1))%mod;
			ans%=mod;
		}
		ans=(ans+mod)%mod;
		printf("%lld\n",ans);
	}
}

心得總結

這道題除了基本套路以外,告訴我們(可能之後這個也是一個套路了):

1.乘積化爲指數和式

2.學會提出一部分可以預處理的函數

3.預處理方式有兩種:1.篩法。2.倍數插入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章