[SDOI2017]数字表格 (莫比乌斯反演)
链接:https://ac.nowcoder.com/acm/problem/20391
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
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取模。
输入描述:
有多组测试数据。第一个一个数T,表示数据组数。
接下来T行,每行两个数n,m
T ≤ 1000,1 ≤ n,m ≤ 10^6
输出描述:
输出T行,第i行的数是第i组数据的结果
示例1
输出
复制1 6 960
解题思路:
然后预处理出括号里前缀就可以了
代码:
#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int maxn=1e6+7; const int mod=1e9+7; int n,m,tot,prime[maxn],mu[maxn]; ll sum[maxn],f[maxn],g[maxn]; ll qpow(ll a,ll b){ ll res=1; while(b){ if(b&1) res=res*a%mod; a=a*a%mod; b>>=1; } return res; } void getMobius(int N){ for(int i=1;i<=N;i++) prime[i]=1; mu[1]=1; f[1]=1; g[1]=1; sum[0]=sum[1]=1; for(int i=2;i<=N;i++){ f[i]=(f[i-2]+f[i-1])%mod; g[i]=qpow(f[i],mod-2); sum[i]=1; if(prime[i]){ mu[i]=-1; prime[tot++]=i; } for(int j=0;j<tot&&i*prime[j]<=N;j++){ prime[i*prime[j]]=0; if(i%prime[j]==0){ mu[i*prime[j]]=0; break; } mu[i*prime[j]]=-mu[i]; } } for(int i=1;i<=N;i++){ //预处理前缀 for(int j=i;j<=N;j+=i){ if(mu[j/i]==0)continue; if(mu[j/i]==1) sum[j]=sum[j]*f[i]%mod; else sum[j]=sum[j]*g[i]%mod; } } for(int i=1;i<=N;i++) sum[i]=sum[i]*sum[i-1]%mod; } ll solve(int a,int b){ ll res=1; if(a>b) swap(a,b); for(int l=1,r;l<=a;l=r+1){ r=min(a/(a/l),b/(b/l)); ll inv=sum[r]*qpow(sum[l-1],mod-2)%mod; //注意除sum[l-1],需要转化成乘它对mod的逆元 res=res*qpow(inv,1ll*(a/l)*(b/l)%(mod-1))%mod; } return res; } int main(){ int T; scanf("%d",&T); getMobius(1e6); while(T--){ scanf("%d%d",&n,&m); printf("%lld\n",solve(n,m)); } return 0; }