BZOJ4816 [Sdoi2017]数字表格 数论 莫比乌斯反演

原文链接http://www.cnblogs.com/zhouzhendong/p/8666106.html

题目传送门 - BZOJ4816

题意

  定义$f(0)=0,f(1)=1,f(i)=f(i-1)+f(i-2)$。

  $T$组数据,每组数据两个整数$n,m$,求$\prod_{i=1}^n\prod_{j=1}^m f(\gcd(i,j))$。

  $T\leq 1000,1\leq n,m \leq 10^6$

题解

  先推一波式子。

  $$\prod_{i=1}^n\prod_{j=1}^m f(\gcd(i,j))\\=\prod_{d=1}^n f(d)^{\sum_{i=1}^{\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\left\lfloor\frac md\right\rfloor}[\gcd(i,j)=1]}\\=\prod_{d=1}^n f(d)^{\sum_{i=1}^{\left\lfloor\frac nd\right\rfloor}\sum_{j=1}^{\left\lfloor\frac md\right\rfloor}\sum_{p|i,p|j}\mu(p)}\\=\prod_{d=1}^n f(d)^{\sum_{p=1}^{\left\lfloor\frac nd\right\rfloor}\mu(p)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor}$$

  设$D=pd$。

  $$\prod_{i=1}^n\prod_{j=1}^m f(\gcd(i,j))\\=\prod_{d=1}^n f(d)^{\sum_{p=1}^{\left\lfloor\frac nd\right\rfloor}\mu(p)\left\lfloor\frac{n}{pd}\right\rfloor\left\lfloor\frac{m}{pd}\right\rfloor}\\=\prod_{D=1}^{n}(\prod_{d|D}f(d)^{\mu(\frac Dd)})^{\left\lfloor\frac nD\right\rfloor\left\lfloor\frac mD\right\rfloor}$$

  先顺手预处理$\mu$。

  于是我们先预处理$f(x)$以及$f(x)$的逆元,然后再$O(n \log n)$预处理出所有$g(D)=\prod_{d|D}f(d)^{\mu(\frac Dd)}$。

  然后再预处理出$g$的前缀积以及前缀积的逆元。

  这些的复杂度都是$O(n \log n)$。

  然后回答一个询问的时候再整除分块一下,单次询问复杂度为$O(\sqrt n \log n)$。

  所以总复杂度为$O(n \log n+T\sqrt n \log n)$。

  所有测试点的总时限开了50s。

  哈哈应该稳过了吧!

  但是!!!!!!

 

 

 

 

 

 

 

 

 

 

 

 

 

  BZOJ毒!瘤!卡!常!!!!!!

  于是我们需要常数优化。

  考虑处理一个序列的逆元。

  我们求出当前序列的前缀积是$O(n)$的。

  记$a$为原序列。

  记$inv_i$为的$i$项的逆元。

  记$presum_i$为序列前$i$项的前缀积。

  记$preinv_i$为序列钱$i$项的前缀积的逆元。

  则:

  $$inv_i=preinv_i\times presum_{i-1}$$

  $$preinv_{i-1}=preinv_i\times a_i$$

  于是你就可以倒着来求逆元了,复杂度$O(n)$。

  但是求$g$的时候复杂度还是$O(n \log n)$。

  所以总的复杂度还是$O(n \log n+T\sqrt n \log n)$。

  但是你卡常数了!!!

  你过了!!!

  QAQ

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+5,mod=1e9+7;
LL prime[N],u[N],pcnt;
bool isprime[N];
LL f[N],pref[N],invf[N],g[N],preg[N],invg[N];
int T,n,m;
LL Pow(LL a,LL b){
	if (!b)
		return 1LL;
	LL x=Pow(a,b/2);
	x=x*x%mod;
	if (b&1LL)
		x=x*a%mod;
	return x;
}
LL Inv(LL a){
	return Pow(a,mod-2);
}
void get_prime(int n){
	memset(isprime,true,sizeof isprime);
	u[1]=1,pcnt=isprime[0]=isprime[1]=0;
	for (int i=2;i<=n;i++){
		if (isprime[i])
			prime[++pcnt]=i,u[i]=-1;
		for (int j=1;j<=pcnt&&i*prime[j]<=n;j++){
			isprime[i*prime[j]]=0;
			if (i%prime[j])
				u[i*prime[j]]=-u[i];
			else {
				u[i*prime[j]]=0;
				break;
			}
		}
	}
}
void init(int n){
	get_prime(n);
	f[0]=0,f[1]=invf[1]=g[0]=1;
	for (int i=2;i<=n;i++)
		f[i]=(f[i-1]+f[i-2])%mod;
	pref[0]=1;
	for (int i=1;i<=n;i++)
		pref[i]=pref[i-1]*f[i]%mod;
	LL v=Inv(pref[n]);
	for (int i=n;i>=1;i--)
		invf[i]=v*pref[i-1]%mod,v=v*f[i]%mod;
	for (int i=1;i<=n;i++)
		g[i]=1;
	for (int i=1;i<=n;i++)
		for (int j=1;i*j<=n;j++){
			if (u[j]==-1)
				g[i*j]=g[i*j]*invf[i]%mod;
			if (u[j]==1)
				g[i*j]=g[i*j]*f[i]%mod;
		}
	preg[0]=1;
	for (int i=1;i<=n;i++)
		preg[i]=preg[i-1]*g[i]%mod;
	invg[n]=Inv(preg[n]);
	for (int i=n-1;i>=0;i--)
		invg[i]=invg[i+1]*g[i+1]%mod;
}
int main(){
	init(1e6);
	scanf("%d",&T);
	while (T--){
		scanf("%d%d",&n,&m);
		if (n>m)
			swap(n,m);
		LL ans=1;
		for (int D=1,i;D<=n;D=i+1){
			i=min(n/(n/D),m/(m/D));
			ans=ans*Pow(preg[i]*invg[D-1]%mod,1LL*(n/D)*(m/D)%(mod-1))%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

  

posted @ 2018-03-28 20:34  zzd233  阅读(598)  评论(0编辑  收藏  举报