[SDOI2017]數字表格 --- 套路反演
阿新 • • 發佈:2018-06-16
register log 計算 span 由於 ali scanf nop 復雜
[SDOI2017]數字表格
由於使用markdown的關系
我無法很好的掌控格式,見諒
對於這麽簡單的一道題竟然能在洛谷混到黑,我感到無語
\(\begin{align*} \prod\limits^{n}_{i=1} \prod\limits^{m}_{j=1} fi[gcd(i,j)] &= \prod\limits^{n}_{d=1} fi[d]^{\sum\limits_{e=1}^{n} [n/de][m/de]\mu(e)} \\ &= \prod\limits^{n}_{T = 1} (\prod\limits_{d|T} fi[d]^{\mu(T/d)})^{[n/T][m/T]} \end{align*}\)
兩步化完式子後
只要預處理函數\(f\prod\limits_{d|n} fi[d]^{\mu(n/d)}\)的前綴積就行
可以做到\(O(n)\)求\(fi\),\(O(n)\)求\(\mu\),枚舉因子\(O(n logn)\)求這個函數,\(O(n)\)計算前綴積
為了方便,我們同時求出他們的逆元即可
復雜度\(O(n logn + T\sqrt n \log n)\)
代碼
#include <cstdio>
#include <iostream>
#define sid 1000050
#define ll long long
#define mod 1000000007
#define ri register int
using namespace std;
const int N = 1000000;
ll fi[sid], f[sid], iv[sid];
int mu[sid], pr[sid], nop[sid], pp, tot;
int read() { scanf("%d", &pp); return pp; }
ll qpow(ll a, ll k) {
ll ret = 1;
while(k) {
if(k & 1) ret = (ret * a) % mod;
a = (a * a) % mod; k >>= 1;
}
return ret;
}
void Get_Fib() {
fi[1] = 1; fi[2] = 1;
for(ri i = 3; i <= N; i ++)
fi[i] = (fi[i - 1] + fi[i - 2]) % mod;
}
void Get_Mu() {
mu[1] = 1;
for(ri i = 2; i <= N; i ++) {
if(!nop[i]) { pr[++ tot] = i; mu[i] = -1; }
for(ri j = 1; j <= tot; j ++) {
int h = i * pr[j];
if(h > N) break; nop[h] = 1;
if(i % pr[j] == 0) { mu[h] = 0; break; }
else mu[h] = -mu[i];
}
}
}
void Get_f() {
for(ri i = 1; i <= N; i ++) f[i] = 1;
for(ri i = 1; i <= N; i ++) {
ll inv = qpow(fi[i], mod - 2);
for(ri j = i; j <= N; j += i)
if(mu[j / i] == -1) f[j] = (f[j] * inv) % mod;
else if(mu[j / i]) f[j] = (f[j] * fi[i]) % mod;
}
f[0] = 1; iv[0] = 1;
for(ri i = 1; i <= N; i ++) f[i] = (f[i] * f[i - 1]) % mod;
for(ri i = 1; i <= N; i ++) iv[i] = qpow(f[i], mod - 2);
}
ll Solve(int n, int m) {
ll ret = 1;
if(n > m) swap(n, m);
for(ri i = 1, j; i <= n; i = j + 1) {
j = min(n / (n / i), m / (m / i));
ret = ret * qpow(f[j] * iv[i - 1] % mod, 1ll * (n / i) * (m / i)) % mod;
}
return ret;
}
int main() {
Get_Fib(); Get_Mu(); Get_f();
int Tt = read();
while(Tt --) {
int n = read(), m = read();
printf("%lld\n", Solve(n, m));
}
return 0;
}
[SDOI2017]數字表格 --- 套路反演