1. 程式人生 > >BZOJ4816: [Sdoi2017]數字表格

BZOJ4816: [Sdoi2017]數字表格

class main ans esp set post type lin int

問題:

  [n/k]/d==[n/(kd)];
  線性篩正確性證明
  這麽求逆元Right?a=k*p;
  1LL轉化作用域
  long long做數組下標

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long Lint;
const int maxn=1000009;
const int mm=1000000007;

int T;
Lint n,m;

Lint ksm(Lint a,Lint p){
	Lint ret=1;
	for(;p;p>>=1,a=a*a%mm){
		if(p&1)ret=ret*a%mm;
	}
	return ret;
}
Lint inv(Lint x){
	return ksm(x,mm-2);
}
Lint g[maxn];
Lint invg[maxn];

Lint prime[maxn];
int vis[maxn],cnt;
int mu[maxn];
Lint f[maxn];
int Lineshake(){
	vis[1]=1;mu[1]=1;
	for(int i=2;i<=1000000;++i){
		if(!vis[i]){
			prime[++cnt]=i;
			mu[i]=-1;
		}
		for(int j=1;(j<=cnt)&&(i*prime[j]<=1000000);++j){
			vis[i*prime[j]]=1;
			if(i%prime[j]==0){
				mu[i*prime[j]]=0;break;
			}
			mu[i*prime[j]]=-mu[i];
		}
	}
	
	f[0]=0;f[1]=1;
	for(int i=2;i<=1000000;++i)f[i]=(f[i-1]+f[i-2])%mm;
	
	for(int i=0;i<=1000000;++i)g[i]=1;
	for(int d=1;d<=1000000;++d){
		Lint invf=inv(f[d]);
		for(int k=1;k*d<=1000000;++k){
			if(mu[k]==1){
				g[k*d]=g[k*d]*f[d]%mm;
			}
			if(mu[k]==-1){
				g[k*d]=g[k*d]*invf%mm;
			}
		}
	}
	for(int i=1;i<=1000000;++i)g[i]=g[i-1]*g[i]%mm;
	for(int i=0;i<=1000000;++i)invg[i]=inv(g[i]);
} 

int Init(){
	cnt=0;
	memset(g,0,sizeof(g));
	memset(vis,0,sizeof(vis));
}

int Getans(){
	if(n>m)swap(n,m);
	Lint ret=1;
	Lint last;
	for(int i=1;i<=n;i=last+1){
		last=min(n/(n/i),m/(m/i));
		ret=ret*ksm(g[last]*invg[i-1]%mm,(n/i)*1LL*(m/i))%mm;
	}
	return (int)ret%mm;
}

int main(){
	Init();
	Lineshake();
	scanf("%d",&T);
	while(T--){
		scanf("%lld%lld",&n,&m);
		printf("%d\n",Getans());
	}
	return 0;
}

  

BZOJ4816: [Sdoi2017]數字表格