【刷題記錄】SDOI2017數字表格
阿新 • • 發佈:2018-12-26
https://www.luogu.org/problemnew/show/P3704
題目描述
Doris剛剛學習了fibonacci數列。用f[i]表示數列的第i項,那麼
f[0]=1,f[1]=1,
f[n]=f[n-1]+f[n-2]
Doris用老師的超級計算機生成了一個n×m的表格,
第i行第j列的格子中的數是f[gcd(i,j)],其中gcd(i,j)表示i,j的最大公約數。
Doris的表格中共有n×m個數,她想知道這些數的乘積是多少。
答案對10^9+7取模。
莫比烏斯反演
直接上表達式(F(i)表示斐波那契數列第I項)
分子是莫比烏斯反演中常見套路
中間括號可以字首積(理論上也可以線段樹)但是注意使用字首積需要處理逆元
再使用快速乘
這樣總時間複雜度
程式碼
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define mod 1000000007
#define N 1000000
#define ll long long
int f[N+6],pre[N+6],prime[N+6],mu[N+6],tot;
bool notprime[N+6];
int fastpow(int x,int n)
{
if(!n)return 1;
int ans = fastpow(x,n/2);
ans = ((ll)ans *ans)%mod;
if(n&1)
ans = ((ll)ans *x)%mod;
return ans;
}
int exgcd(ll &x,ll &y,int a, int b)
{
if(b==0)
{
x = 1;
y = 0;
return a;
}
int ans = exgcd(x,y,b,a%b);
ll t = x;
x = y;
y = t- (a/b)*y;
return ans;
}
int inv(int m)
{
ll x,y;
exgcd(x,y,m,mod);
return (int)(((x%mod)+mod)%mod);
}
void init()
{
pre[0]=pre[1]=f[1]=notprime[1]=mu[1]=1;
for(int i = 2; i <= N; i ++)
{
pre[i]=1;
f[i]=((ll)f[i-1]+f[i-2])%mod;
if(!notprime[i])
{
prime[++tot]=i;
mu[i]=-1;
}
for(int j = 1 ; j <= tot && i*prime[j] <= N ; j ++)
{
notprime[i*prime[j]]=1;
if(i%prime[j]!=0)mu[i*prime[j]]=-mu[i];
else
{
break;
}
}
}
for(int i = 1; i <= N ; i ++)
for(int j = i; j <= N ; j += i)
{
int t = f[i];
if(mu[j/i]==-1)
{
t = inv(t);
}
else if(mu[j/i]==0)
{
t = 1;
}
pre[j]=((ll)pre[j]*t)%mod;
}
for(int i = 1; i <= N; i ++)
{
pre[i]=((ll)pre[i]*pre[i-1])%mod;
}
}
int main()
{
int t;
init();
scanf("%d",&t);
while(t--)
{
int ans = 1, n,m;
scanf("%d%d",&n,&m);
if(n>m)swap(m,n);
for(int i = 1,last; i <= n; i = last +1 )
{
last = min(n/(n/i),m/(m/i));
ans = ((ll)ans* fastpow((int)(((ll)pre[last]*inv(pre[i-1]))%mod),(n/i)*(m/i)))%mod;
}
printf("%d\n",ans);
}
}