Time Limit: 50 Sec
Memory Limit: 128 MB
Description
Doris刚刚学习了fibonacci数列。用f[i]表示数列的第i项,那么
f[0]=0,f[1]=1
f[n]=f[n-1]+f[n-2],n>=2
Doris用老师的超级计算机生成了一个n×m的表格,第i行第j列的格子中的数是f[gcd(i,j)],其中gcd(i,j)表示i,
j的最大公约数。Doris的表格中共有n×m个数,她想知道这些数的乘积是多少。答案对10^9+7取模。
Input
有多组测试数据。
第一个一个数T,表示数据组数。
接下来T行,每行两个数n,m
T<=1000,1<=n,m<=10^6
Output
输出T行,第i行的数是第i组数据的结果
题目分析
令
g
(
d
)
g(d)
g(d)表示
1
≤
x
≤
n
,
1
≤
y
≤
m
1\leq x\leq n,1\leq y\leq m
1≤x≤n,1≤y≤m内有多少个
(
x
,
y
)
(x,y)
(x,y)满足
g
c
d
(
x
,
y
)
=
d
gcd(x,y)=d
gcd(x,y)=d
那么可以转化目标表达式
∏
i
=
1
n
∏
j
=
1
m
f
(
g
c
d
(
i
,
j
)
)
=
∏
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
g
(
d
)
\prod_{i=1}^n\prod_{j=1}^mf(gcd(i,j))=\prod_{d=1}^{min(n,m)}f(d)^{g(d)}
i=1∏nj=1∏mf(gcd(i,j))=d=1∏min(n,m)f(d)g(d)
这个 g ( d ) g(d) g(d)几乎是莫比乌斯反演考烂了的套路了
g ( d ) = ∑ d ∣ k μ ( k d ) ⌊ n k ⌋ ⌊ m k ⌋ = ∑ t = 1 m i n ( ⌊ n d ⌋ , ⌊ m d ⌋ ) μ ( t ) ⌊ n t d ⌋ ⌊ m t d ⌋ g(d)=\sum_{d|k}\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor=\sum_{t=1}^{min(\lfloor\frac{n}{d}\rfloor,\lfloor\frac{m}{d}\rfloor)}\mu(t)\lfloor\frac{n}{td}\rfloor\lfloor\frac{m}{td}\rfloor g(d)=d∣k∑μ(dk)⌊kn⌋⌊km⌋=t=1∑min(⌊dn⌋,⌊dm⌋)μ(t)⌊tdn⌋⌊tdm⌋
带入原式
∏
d
=
1
m
i
n
(
n
,
m
)
f
(
d
)
∑
d
∣
k
μ
(
k
d
)
⌊
n
k
⌋
⌊
m
k
⌋
=
∏
d
=
1
m
i
n
(
n
,
m
)
∏
d
∣
k
f
(
d
)
μ
(
k
d
)
⌊
n
k
⌋
⌊
m
k
⌋
\prod_{d=1}^{min(n,m)}f(d)^{\sum_{d|k}\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor}=\prod_{d=1}^{min(n,m)}\prod_{d|k}f(d)^{\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor}
d=1∏min(n,m)f(d)∑d∣kμ(dk)⌊kn⌋⌊km⌋=d=1∏min(n,m)d∣k∏f(d)μ(dk)⌊kn⌋⌊km⌋
交换 k , d k,d k,d的枚举顺序
∏ k = 1 m i n ( n , m ) ∏ d ∣ k f ( d ) μ ( k d ) ⌊ n k ⌋ ⌊ m k ⌋ \prod_{k=1}^{min(n,m)}\prod_{d|k}f(d)^{\mu(\frac{k}{d})\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor} k=1∏min(n,m)d∣k∏f(d)μ(dk)⌊kn⌋⌊km⌋
看着似乎有点复杂,把中间那坨抽出来表示
令
h
(
k
)
=
∏
d
∣
k
f
(
d
)
μ
(
k
d
)
h(k)=\prod_{d|k}f(d)^{\mu(\frac{k}{d})}
h(k)=∏d∣kf(d)μ(dk),那么表达式为
∏
k
=
1
m
i
n
(
n
,
m
)
h
(
k
)
⌊
n
k
⌋
⌊
m
k
⌋
\prod_{k=1}^{min(n,m)}h(k)^{\lfloor\frac{n}{k}\rfloor\lfloor\frac{m}{k}\rfloor}
∏k=1min(n,m)h(k)⌊kn⌋⌊km⌋
预处理
h
(
k
)
h(k)
h(k)的前缀积,每个询问用整除分块优化后复杂度
O
(
n
l
o
g
n
)
O(\sqrt{n}logn)
O(nlogn),这里log是快速幂计算的复杂度
h
(
k
)
h(k)
h(k)可以用枚举倍数的方法预处理,复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),莫比乌斯函数预处理
O
(
n
)
O(n)
O(n)
最后总复杂度
O
(
n
l
o
g
n
+
n
+
T
n
l
o
g
n
)
O(nlogn+n+T\sqrt{n}logn)
O(nlogn+n+Tnlogn)
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const lt mod=1e9+7;
const int maxn=1000010;
int T,miu[maxn];
int vis[maxn],prim[maxn],cnt;
lt f[maxn],h[maxn];
lt sum[maxn];
lt qpow(lt a,lt k)
{
lt res=1;
while(k>0){
if(k&1) res=(res*a)%mod;
a=(a*a)%mod; k>>=1;
}
return res;
}
void calc(int n)
{
miu[1]=1;
for(int i=2;i<=n;++i)
{
if(!vis[i]) prim[++cnt]=i,miu[i]=-1;
for(int j=1;j<=cnt;++j)
{
if(i*prim[j]>n) break;
vis[i*prim[j]]=1;
if(i%prim[j]==0) break;
else miu[i*prim[j]]=-miu[i];
}
}
f[0]=0; f[1]=1;
for(int i=2;i<=n;++i)
f[i]=(f[i-1]+f[i-2])%mod;
for(int i=0;i<=n;++i) h[i]=1;
for(int i=1;i<=n;++i)
{
lt inv=1ll*qpow(f[i],mod-2);//先计算f[i]逆元,下面枚举遇到miu为-1时不用重复计算
for(int j=i;j<=n;j+=i)
{
if(miu[j/i]==-1) h[j]=h[j]*inv%mod;
else if(miu[j/i]==1) h[j]=h[j]*f[i]%mod;
}
}
for(int i=1;i<=n;++i)
h[i]=h[i-1]*h[i]%mod;
}
lt query(int n,int m)
{
lt res=1; int lim=min(n,m);
for(int ll=1,rr;ll<=lim;ll=rr+1)
{
rr=min(n/(n/ll),m/(m/ll));
lt Mul=h[rr]*qpow(h[ll-1],mod-2)%mod , ki=1ll*(n/ll)*(m/ll);
res=res*qpow(Mul,ki)%mod;
}
return res;
}
int main()
{
T=read(); calc(maxn-5);
while(T--)
{
int n=read(),m=read();
printf("%lld\n",query(n,m));
}
return 0;
}