bzoj 4816

这题是莫比乌斯反演的典型题也是很有趣的题。

题意:求\prod_{k=1}^{min(n,m)}f(k)^{\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==k]},其中f为为斐波那契数列

那么首先观察一下指数,发现是我们熟悉的形式,可以转化成这样的形式:\prod_{k=1}^{min(n,m)}f(k)^{\sum_{d=1}^{min([n/k],[m/k])}\mu(d)*[n/k]*[m/k]}

令T=kd,且假设n<m,有:

\prod_{T=1}^{n}\prod_{k|T}f(k)^{\mu(T/k)*[n/T]*[m/T]}

g(T)=\prod_{k|T}f(k)^{\mu(T/k)}

则原式=\prod_{T=1}^{n}g(T)^{[n/T]*[m/T]}

这样的话我们的步骤就是这样的:

线性筛出莫比乌斯函数,同时递推求出f

然后利用f和莫比乌斯函数求出g(枚举倍数,这样把时间复杂度控制在调和级数级别),注意到有时会出现分数(莫比乌斯函数值为-1时),所以对上面的每个f需要求出对应地逆元(费马小定理)

然后对g求出前缀积,这样就可以利用数论分块在根号级的时间内求出答案了,但由于是乘积式,所以在提取一段乘积的时候会出现除法,所以还要对求出的前缀积求出逆元。

注意上面的都是要预处理出的内容

然后就水到渠成了

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define mode 1000000007
using namespace std;
ll g[1000005];
ll f[1000005];
ll inv[1000005];
int mu[1000005];
int pri[1000005];
ll mul[1000005];
ll minv[1000005];
int cnt=0;
bool used[1000005];
int T,n,m;
ll pow_mul(ll x,ll y)
{
	ll ans=1;
	while(y)
	{
		if(y&1)
		{
			ans*=x;
			ans%=mode;
		}
		x*=x;
		x%=mode;
		y/=2;
	}
	return ans;
}
void init()
{
	mu[1]=1;
	f[1]=1;
	g[1]=1;
	inv[1]=1;
	for(int i=2;i<=1000000;i++)
	{
		f[i]=f[i-1]+f[i-2];
		f[i]%=mode;
		g[i]=1;
		inv[i]=pow_mul(f[i],mode-2);
		if(!used[i])
		{
			pri[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;j<=cnt&&i*pri[j]<=1000000;j++)
		{
			used[i*pri[j]]=1;
			if(i%pri[j]==0)
			{
				mu[i*pri[j]]=0;
				break;
			}
			mu[i*pri[j]]=-mu[i];
		}
	}
	for(int i=1;i<=1000000;i++)
	{
		for(int j=1;i*j<=1000000;j++)
		{
			if(!mu[j])
			{
				continue;
			}else if(mu[j]==1)
			{
				g[i*j]*=f[i];
				g[i*j]%=mode;
			}else
			{
				g[i*j]*=inv[i];
				g[i*j]%=mode;
			}
		} 
	}
	mul[0]=1;
	minv[0]=1;
	for(int i=1;i<=1000000;i++)
	{
		mul[i]=mul[i-1]*g[i]%mode;
		minv[i]=pow_mul(mul[i],mode-2);
	}
}
ll solve(int x,int y)
{
	if(x>y)
	{
		swap(x,y);
	}
	ll ans=1;
	int last=0;
	for(int i=1;i<=x;i=last+1)
	{
		last=min(x/(x/i),y/(y/i));
		ans*=pow_mul(mul[last]*minv[i-1]%mode,(ll)(x/i)*(ll)(y/i)%(mode-1));
		ans%=mode;
	}
	return ans;
}
int main()
{
	init();
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		printf("%lld\n",solve(n,m));
	}
	return 0;
}

 

posted @ 2019-04-10 18:30  lleozhang  Views(99)  Comments(0Edit  收藏
levels of contents