BZOJ 3456: 城市規劃 與 算法介紹(多項式求逆元 , dp)
- 題面 :
求有 \(n\) 個點的無向有標號連通圖個數 . \((1 \le n \le 1.3 * 10^5)\)
- 題解 :
首先考慮 dp ... 直接算可行的方案數 , 容易算重復 .
我們用總方案數減去不可行的方案數就行了 (容斥)
令 \(f_i\) 為有 \(i\) 個點的無向有標號連通圖個數 .
考慮 \(1\) 號點的聯通塊大小 , 聯通塊外的點之間邊任意 但 不能與 \(1\) 有間接聯系 .
那麽就有
\[\displaystyle f_i = 2^{\binom i 2} - \sum_{j=1}^{i-1} f_j \times \binom {i-1}{j-1} \times 2^{\binom{i-j}{2}}\]
這個可以直接用 CDQ分治FFT 求 , 但我不會 ...
那認真考慮一下這個式子 qwq
先展開組合數
\[\displaystyle f_i=2^{\binom{i}{2}} - \sum_{j=1}^{i-1} f_j \times \frac{(i-1)!}{(j-1)!(i-j)!} \times 2^{\binom {i-j}{2}}\]
除去 \((i-1)!\)
\[\displaystyle \frac{f_i}{(i-1)!} = \frac{2^{\binom i 2}}{(i-1)!} - \sum_{j=1}^{i-1} \frac{f_j\times2^{\binom{i-j}{2}}}{(j-1)!(i-j)!}\]
移項合並
\[\displaystyle \sum_{j=1}^{i} \frac{f_j \times 2^{\binom {i-j}{2}}}{(j-1)!\times(i-j)!} = \frac{2^{\binom i 2}}{(i-1)!}\]
左邊我們觀察一下不難發現是一個卷積形式 .
令 \(\displaystyle A=\sum_{i=1}^{n} \frac{f_i}{(i-1)!} x^i\)
\(\displaystyle B=\sum_{i=0}^{n} \frac{2^{\binom {i}{2}}}{i!} x^i\)
\(\displaystyle C = \sum_{i=1}^{n} \frac{2^{\binom{i}{2}}}{(i-1)!} x^i\)
就有 \(A * B = C \Rightarrow A=C * B^{-1}\)
我們只需要求出 \(B\) 在 \(x^n\) 下的逆元就行了 qwq
怎麽求呢 :
多項式求逆 :
如果 \(B\) 為 \(A\) 在 \(x^n\) 意義下的逆元 , 那麽數學表達就是 :
\[A * B \equiv 1 \pmod {x^n}\]
假設我們已經求出了\(B\) 在 \(\displaystyle x^{\frac{n}{2}}\) 的逆元 \(B‘\) . (我們常常令 \(n\) 為 \(2^k\) )
\(\displaystyle A * B‘ \equiv 1 \pmod {x^\frac{n}{2}} \tag{1}\)
我們之前那個 \(x^{\frac{n}{2}}\) 意義下也成立 ... 就有
\[\displaystyle A * B \equiv 1 \pmod {x^{\frac{n}{2}}} \tag{2}\]
讓 \((2)-(1)\) 就有
\[B - B‘\equiv 0 \pmod{x^{\frac n 2}}\]
然後把它左右平方一下 (此處 \(x^\frac{n}{2}\) 也可平方成 \(x^n\) )
\[B^2 -2BB‘ +B‘^2 \equiv 0 \pmod {x^{n}}\]
乘上一個 \(A\) 就得到
\[B \equiv 2B‘ + A * B‘^2 \pmod {x^n}\]
然後遞歸求解就行了 .
那麽復雜度就是 \(T(n) = T(\frac{n}{2}) + O(n \log n) =O(n \log n)\) ...
代碼在這道題程序中有 ...
- 代碼 :
/**************************************************************
Problem: 3456
User: DOFY
Language: C++
Result: Accepted
Time:11956 ms
Memory:107796 kb
****************************************************************/
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
typedef long long ll;
inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == ‘-‘) fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("3456.in", "r", stdin);
freopen ("3456.out", "w", stdout);
#endif
}
const int N = (1 << 21) + 5, Mod = 1004535809;
ll fpm(ll x, int power) {
ll res = 1;
for (; power; power >>= 1, (x *= x) %= Mod)
if (power & 1) (res *= x) %= Mod;
return res;
}
ll fac[N], ifac[N];
inline ll C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
return fac[n] * ifac[m] % Mod * ifac[n - m] % Mod;
}
void Init(int maxn) {
fac[0] = ifac[0] = 1;
For (i, 1, maxn) fac[i] = fac[i - 1] * i % Mod;
ifac[maxn] = fpm(fac[maxn], Mod - 2);
Fordown (i, maxn - 1, 1) ifac[i] = ifac[i + 1] * (i + 1) % Mod;
}
inline int Add(int a, int b) { return ((a += b) >= Mod) ? a - Mod : a; }
struct Number_Theory_Transform {
int pow3[N], invpow3[N];
inline void Init(int maxn) {
for (int i = 2; i <= maxn; i <<= 1)
pow3[i] = fpm(3, (Mod - 1) / i), invpow3[i] = fpm(pow3[i], Mod - 2);
}
int n, rev[N];
inline void Get_Rev() {
int cnt = 0; for (int i = 1; i < n; i <<= 1) ++ cnt;
For (i, 0, n - 1) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1));
}
void NTT(int P[], int opt) {
For (i, 0, n - 1) if (i < rev[i]) swap(P[i], P[rev[i]]);
for (int i = 2; i <= n; i <<= 1) {
int p = i >> 1, Wi = opt == 1 ? pow3[i] : invpow3[i];
for (int j = 0; j < n; j += i)
for (int k = 0, x = 1; k < p; ++ k, x = 1ll * x * Wi % Mod) {
int u = P[j + k], v = 1ll * x * P[j + k + p] % Mod;
P[j + k] = Add(u, v);
P[j + k + p] = Add(u, Mod - v);
}
}
if (opt == -1) {
int invn = fpm(n, Mod - 2);
For (i, 0, n - 1) P[i] = 1ll * P[i] * invn % Mod;
}
}
int A[N], B[N];
void Mult(int a[], int b[], int c[], int len) {
for (n = 1; n <= len; n <<= 1); Get_Rev();
For (i, 0, n - 1) A[i] = a[i], B[i] = b[i];
NTT(A, 1); NTT(B, 1);
For (i, 0, n - 1) A[i] = 1ll * A[i] * B[i] % Mod;
NTT(A, -1);
For (i, 0, n - 1) c[i] = A[i];
}
void Get_Inv(int a[], int b[], int len) {
if (len == 1) { b[0] = fpm(a[0], Mod - 2); return ; }
Get_Inv(a, b, len >> 1);
n = len << 1; Get_Rev();
For (i, 0, len - 1) A[i] = a[i], B[i] = b[i];
NTT(A, 1); NTT(B, 1);
For (i, 0, n - 1) A[i] = 1ll * A[i] * B[i] % Mod * B[i] % Mod;
NTT(A, - 1);
For (i, 0, len - 1)
b[i] = Add(Add(b[i], b[i]), Mod - A[i]);
}
} T;
int a[N], b[N], c[N], tmp[N], n;
int Edge(int x, int nowmod) { return 1ll * x * (x - 1) / 2 % nowmod; }
int main () {
File();
n = read();
T.Init(1 << 20); Init(n);
For (i, 0, n)
b[i] = fpm(2, Edge(i, Mod - 1)) * ifac[i] % Mod;
For (i, 0, n)
c[i] = fpm(2, Edge(i, Mod - 1)) * ifac[i - 1] % Mod;
int len = 1; for (; len <= n; len <<= 1);
T.Get_Inv(b, tmp, len); T.Mult(tmp, c, a, len);
printf ("%lld\n", 1ll * a[n] * fac[n - 1] % Mod);
return 0;
}
BZOJ 3456: 城市規劃 與 算法介紹(多項式求逆元 , dp)