[SDOI2017]数字表格

Posted by Dispwnl on August 28, 2018

题目

题目描述

Doris刚刚学习了fibonacci数列。用$f[i]$表示数列的第$i$项,那么

$f[0]=0,f[1]=1,$

$f[n]=f[n-1]+f[n-2],n\geq 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$行,第$i$行的数是第$i$组数据的结果

输入输出样例

输入样例#1:

3
2 3
4 5
6 7

输出样例#1:

1
6
960

说明

对$10\%$的数据,1\leq n,m\leq 100

对$30\%$的数据,$1\leq n,m\leq 1000$

另外存在$30\%$的数据,$T\leq 3$

对$100\%$的数据,$T\leq 1000,1\leq n,m\leq 10^6$

时间限制:5s

内存限制:128MB

题解

题目要求:$\prod_{i=1}^{n}\prod_{j=1}^{m}f[\gcd(i,j)]$

可以化简成:$\prod_{x=1}^{min(n,m)}f[x]^{\sum_{i=1}^{\left\lfloor\frac{n}{x}\right\rfloor}\sum_{j=1}^{\left\lfloor\frac{m}{x}\right\rfloor}[\gcd(i,j)]==1]}$

发现指数可以莫比乌斯反演一下:$\prod_{x=1}^{min(n,m)}f[x]^{\sum_{i=1}^{min(\left\lfloor\frac{n}{x}\right\rfloor,\left\lfloor\frac{m}{x}\right\rfloor)}\mu(i)\left\lfloor\frac{n}{ix}\right\rfloor\left\lfloor\frac{m}{ix}\right\rfloor}$

把$ix$提出来,即有:$\prod_{T=1}^{min(n,m)}\prod_{x\vert T}f[x]^{\mu(\frac{T}{x})\left\lfloor\frac{n}{T}\right\rfloor\left\lfloor\frac{m}{T}\right\rfloor}$

发现可以先处理出来$\prod_{x\vert T}f[x]^{\mu(\frac{T}{x})}$,搞个前缀积,然后整除分块带进去就行了,注意除的时候要求逆元

代码

# include<iostream>
# include<cstring>
# include<cstdio>
# include<algorithm>
# define LL long long
using namespace std;
const int MAX=1e6+1;
const LL mod=1e9+7;
int t,n,m,maxn,tot;
int u[MAX],pm[MAX];
LL f[2][MAX],A[MAX];
bool use[MAX];
LL pow(LL a,LL b)
{
	LL ans=1;
	for(;b;b>>=1,a=a*a%mod)
	  if(b&1) ans=ans*a%mod;
	return ans;
}
int main()
{
	u[1]=1,f[0][1]=f[1][1]=A[0]=A[1]=1;
	for(int i=2;i<MAX;++i)
	  {
	  	A[i]=1,f[0][i]=(f[0][i-1]+f[0][i-2])%mod,f[1][i]=pow(f[0][i],mod-2);
	  	if(!use[i]) pm[++tot]=i,u[i]=-1;
	  	for(int j=1;i*pm[j]<MAX;++j)
	  	  {
	  	  	use[i*pm[j]]=1;
	  	  	if(i%pm[j]==0)
	  	  	{
	  	  		u[i*pm[j]]=0;
	  	  		break;
			}
			u[i*pm[j]]=-u[i];
		  }
	  }
	for(int i=1;i<MAX;++i)
	  for(int j=1;j*i<MAX;++j)
	    if(u[i]==1) A[i*j]=A[i*j]*f[0][j]%mod;
	    else if(u[i]==-1) A[i*j]=A[i*j]*f[1][j]%mod;
	for(int i=1;i<MAX;++i)
	  A[i]=A[i]*A[i-1]%mod;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		if(n>m) swap(n,m);
		LL ans=1;
		for(int l=1,r;l<=n;l=r+1)
		  r=min(n/(n/l),m/(m/l)),ans=ans*pow((A[r]*pow(A[l-1],mod-2))%mod,1ll*(n/l)*(m/l))%mod;
		printf("%lld\n",ans);
	}
	return 0;
}