1. 程式人生 > >[SDOI2017]數字表格 --- 套路反演

[SDOI2017]數字表格 --- 套路反演

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]數字表格 --- 套路反演