[SDOI2017] 数字表格
反演拾遗,设'~'表示min(n,m),推狮子
\[\prod_{i=1}^n\prod_{j=1}^n\mathbb{fib}_{\gcd(a,b)}
=\prod_{d=1}^\sim\mathbb{pow}(\mathbb{fib}_d,\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}\epsilon(\gcd(n,m)))\\
=\prod_{d=1}^\sim\mathbb{pow}(\mathbb{fib}_d,\sum_{t=1}^{\sim/d}\mu(t)\frac{n}{dt}\frac{m}{dt})\\
=\prod_{q=1}^\sim\prod_{d|q}\mathbb{pow}(\mathbb{fib}_d,\mu(\frac{q}{d})\frac{n}{q}\frac{m}{q})\\
=\prod_{q=1}^\sim\mathbb{pow}(\prod_{d|q}\mathbb{pow}(\mathbb{fib}_d,\mu(\frac{q}{d})),\frac{n}{q}\frac{m}{q})
\]
预处理处外层pow的底数设为w[q],求其前缀积,每组询问分块。
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e6+10;
const int P=1e9+7;
inline int qpow(LL x,LL y) {
int c=1;
for(; y; y>>=1,x=x*x%P)
if(y&1) c=x*c%P;
return c;
}
int pri[N],mu[N],fib[N],fiv[N],cnt;
long long w[N];
bool vis[N];
void initial() {
mu[1]=1;
for(int i=2; i<N; ++i) {
if(!vis[i]) pri[++cnt]=i,mu[i]=-1;
for(int j=1; j<=cnt&&i*pri[j]<N; ++j) {
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
else mu[i*pri[j]]=-mu[i];
}
}
fib[1]=w[0]=w[1]=fiv[0]=fiv[1]=1;
for(int i=2; i<N; ++i) {
fib[i]=(fib[i-1]+fib[i-2])%P;
fiv[i]=qpow(fib[i],P-2);
w[i]=1;
}
for(int d=1; d<N; ++d) {
for(int q=d; q<N; q+=d) {
if(mu[q/d]>0) w[q]=w[q]*fib[d]%P;
else if(mu[q/d]<0) w[q]=w[q]*fiv[d]%P;
}
w[d]=w[d-1]*w[d]%P;
}
}
int main() {
initial(); //0.32s
int T,n,m;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
int c=1,sim=min(n,m);
for(int l=1,r; l<=sim; l=r+1) {
r=min(n/(n/l),m/(m/l));
c=1LL*c*qpow(w[r]*qpow(w[l-1],P-2)%P,1LL*n/l*(m/l)%(P-1))%P;
}
printf("%d\n",c);
}
return 0;
}
把\(\mu\)求成\(\varphi\)是要闹哪样啊