第一次见到这种形式,推了两步就被卡住了。没想到还能这么迁移QAQ,真强!
题目设 $f(i)$为第$i$项斐波那契数列
要求计算
$$ans=\prod_{i=1}^n \prod_{j=1}^m f(gcd(i,j))$$
枚举$d=gcd$得到
$$ans=\prod_{d=1}^n f(d)^{\sum_{i=1}^n \sum_{j=1}^m[gcd(i,j)==d]}$$
然后反演得到
$$ans=\prod_{d=1}^n f(d)^{\sum_{T=1}^{\lfloor \frac{n}{d} \rfloor}\mu(T) \lfloor \frac{n}{dT} \rfloor \lfloor \frac{m}{dT} \rfloor}$$
然后呢,令$p=dT$,得到
$$ans=\left( \prod_{d=1}^nf(d)^{\mu(\frac{p}{d})} \right) ^{\sum_{p=1}^n \lfloor \frac{n}{p} \rfloor \lfloor \frac{m}{p} \rfloor}$$
这样我们就发现我们可以对$p$进行分块去搞了
令
$$g(p)=\prod_{d|p} f(d)^{\mu(\frac{p}{d})}$$
然后我们就可以通过预处理$g(i)$和它的前缀积,然后枚举$p$分块去搞,用$last$前缀积除以$i-1$的前缀积就可以得到这一段的乘积,然后将它们对 $\lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor$做快速幂即可
我们怎么预处理$g(i)$呢?
我们可以枚举$d$,然后统计它对它的倍数的贡献
观察式子$f(d)^{\mu(\frac{p}{d})}$,$\mu$的取值只有三种。为$0$时不用管,为$1$时需要乘$f(i)$,为$-1$时需要乘$f(i)^{-1}$
关键问题就是预处理出$f(i)^{-1}$
我们线性推就好了。设$$s[x]=\prod_{i=1}^x f(x)$$ $$rs[x]=s[x]^{-1}$$ $$ni[x]=f(x)^{-1}$$
于是$$rs[x-1]=rs[x]*f[x]$$ $$ni[x]=rs[x]*s[x-1]$$我们只需要求出$rs[n]$然后倒着推一遍就行了(撒花!)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define N 1000100
#define pos(i,a,b) for(LL i=(a);i<=(b);i++)
#define pos2(i,a,b) for(LL i=(a);i>=(b);i--)
using namespace std;
#define LL long long
const int p=(LL)1e9+7;
int t;
LL n,m;
int notprime[N],prime[N],mu[N];
LL x,y;
LL s[N],rs[N],f[N],ni[N];
LL g[N];
void kuogcd(LL a,LL b,LL &x,LL &y){
if(!b){
x=1;y=0;return;
}
kuogcd(b,a%b,x,y);
LL temp=x;x=y;y=temp-a/b*y;
}
LL qpow(LL a,LL k){
LL res=1;
while(k>0){
if(k&1){
res=(res*a)%p;
}
a=(a*a)%p;k>>=1;
}
return res;
}
void get_pre(){
notprime[1]=mu[1]=1;
pos(i,2,N-10){
if(!notprime[i]){
prime[++prime[0]]=i;
mu[i]=-1;
}
for(int j=1;j<=prime[0]&&prime[j]*i<=N-10;j++){
notprime[i*prime[j]]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
f[1]=1;
pos(i,2,N-10) f[i]=(f[i-1]+f[i-2])%p;
s[0]=1;
pos(i,1,N-10){
s[i]=s[i-1]*f[i]%p;
}
kuogcd(s[N-10],p,x,y);
while(x<0) x+=p;
rs[N-10]=x%p;
pos2(i,N-10,1){
rs[i-1]=rs[i]*f[i]%p;
ni[i]=rs[i]*s[i-1]%p;
}
pos(i,1,N-10) g[i]=1;
pos(i,1,N-10){
pos(j,1,N-10){
if(i*j>N-10) break;
int pp=i*j;
if(mu[j]==1) g[pp]=(g[pp]*f[i])%p;
else if(mu[j]==-1) g[pp]=(g[pp]*ni[i])%p;
}
}
g[0]=1;
pos(i,1,N-10) g[i]=(g[i]*g[i-1])%p;
}
LL get_ans(){
LL res(1);
LL last(0);
for(LL i=1;i<=n;i=last+1){
last=min(n/(n/i),m/(m/i));
kuogcd(g[i-1],p,x,y);
while(x<0) x+=p;
res=res*qpow(g[last]*x%p,(n/i)*(m/i))%p;
}
return res%p;
}
int main(){
get_pre();
scanf("%d",&t);
while(t--){
scanf("%lld%lld",&n,&m);if(n>m) n^=m,m^=n,n^=m;
printf("%lld\n",get_ans());
}
return 0;
}