[xsy2309]数字表格

题意:求$\prod\limits_{i=1}^n\prod\limits_{j=1}^mf_{(i,j)}$,其中$f_0=0,f_1=1,f_n=f_{n-1}+f_{n-2}$

很妙的题

假设$n\leq m$,如果我们能找到一个$g$使得$f(n)=\prod\limits_{d|n}g(d)$,那么答案就是$\prod\limits_{d=1}^ng(d)^{\left\lfloor\frac nd\right\rfloor\left\lfloor\frac md\right\rfloor}$

然后直接把莫比乌斯反演搬过来居然也是可以的...我们得到$g(n)=\prod\limits_{d|n}f(d)^{\mu\left(\frac nd\right)}$

于是枚举$d$更新$d$的倍数就预处理出$g$了,时间复杂度$O\left(n\log_2n+T\sqrt n\log_2n\right)$

#include<stdio.h>
typedef long long ll;
const int mod=1000000007,T=1000000;
void swap(int&a,int&b){a^=b^=a^=b;}
int min(int a,int b){return a<b?a:b;}
int mul(int a,int b){return a*(ll)b%mod;}
int pow(int a,ll b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
int pr[T+10],mu[T+10],f[T+10],rf[T+10],g[T+10],rg[T+10];
bool np[T+10];
void sieve(){
	int i,j,M=0;
	mu[1]=1;
	for(i=2;i<=T;i++){
		if(!np[i]){
			pr[++M]=i;
			mu[i]=-1;
		}
		for(j=1;j<=M&&i*pr[j]<=T;j++){
			np[i*pr[j]]=1;
			if(i%pr[j]==0)break;
			mu[i*pr[j]]=-mu[i];
		}
	}
}
int main(){
	int cas,n,m,i,j,nex,s;
	sieve();
	f[0]=0;
	f[1]=1;
	for(i=2;i<=T;i++)f[i]=(f[i-1]+f[i-2])%mod;
	for(i=1;i<=T;i++)rf[i]=pow(f[i],mod-2);
	for(i=1;i<=T;i++)g[i]=1;
	for(i=1;i<=T;i++){
		if(mu[i]){
			for(j=i;j<=T;j+=i)g[j]=mul(g[j],(mu[i]==1?f:rf)[j/i]);
		}
	}
	for(i=2;i<=T;i++)g[i]=mul(g[i],g[i-1]);
	for(i=1;i<=T;i++)rg[i]=pow(g[i],mod-2);
	rg[0]=1;
	scanf("%d",&cas);
	while(cas--){
		scanf("%d%d",&n,&m);
		if(n>m)swap(n,m);
		s=1;
		for(i=1;i<=n;i=nex+1){
			nex=min(n/(n/i),m/(m/i));
			s=mul(s,pow(mul(g[nex],rg[i-1]),(ll)(n/i)*(m/i)));
		}
		printf("%d\n",(s+mod)%mod);
	}
}

转载于:https://www.cnblogs.com/jefflyy/p/9376247.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值