【bzoj3456】城市规划(多项式求逆+dp)

Description

求\(~n~\)个点组成的有标号无向连通图的个数。\(~1 \leq n \leq 13 \times 10 ^ 4~\).

Solution

这道题的弱化版是poj1737, 其中\(n \leq 50\), 先来解决这个弱化版的题。考虑\(~dp~\),直接统计答案难以入手,于是考虑容斥。显然有,符合条件的方案数\(=\)所有方案数\(-\)不符合条件的方案数,而这个不符合条件的方案数就是图没有完全联通的情况。设\(~dp_i~\)表示\(~i~\)个点组成的合法方案数,考虑\(~1~\)号点的联通情况,易得

\[
dp_i = 2 ^ {i \choose 2} - \sum_{j = 1}^{i - 1} {dp_j \times {{i - 1} \choose {j - 1}}} \times 2 ^ {{i - j} \choose 2}
\]

这是什么意思呢...枚举\(~1~\)号点所在的联通块大小\(~j~\), 可以知道这个联通块有\(~dp_j~\)种连法,除了这\(~j~\)个点,剩下的点可以随意连接,而这\(~j - 1~\)个点可以在\(~i - 1~\)个点中随意选择(因为强制选了\(~1~\)号点)。

那么这个简单版就已经解决了,时间复杂度\(~O(n ^ 2)~\), 而且要写高精度。博主很懒就不想写了。这个复杂度的话不是神威的话肯定是跑不过的。于是我们考虑优化,把上面的式子展开,可以得到

\[
dp_i = 2 ^ {i \choose 2} - \sum_{j = 1}^{i - 1} dp_j \times \frac{(i - 1)!}{(j - 1)!(i - j)! } \times {2 ^ {i - j \choose 2}}
\]

同除\(~(i - 1) !~?\), 得

\[
\frac{dp_i}{(i - 1)!} = \frac{2 ^ {i \choose 2}}{(i - 1)!} - \sum_{j = 1}^{i - 1} \frac {dp_j \times {2 ^ {i - j \choose 2}}} {(j - 1)!(i - j)!}
\]

观察一下,这个\(~ \frac{dp_i}{(i - 1)!} ~\)就是那个\(~\sum~\)式子的第\(~i~\)项, 移项得,

\[
\sum_{j = 1}^{i} \frac {dp_j}{(j - 1)!} \times \frac{2 ^ {i - j \choose 2}}{(i - j)!} = \frac{2 ^ {i \choose 2}}{(i - 1)!}
\]

左边是很自然的一个卷积式子。令

\[
h = \sum_{i = 1}^{n} \frac{dp_i}{(i - 1)!} \times x^i~~, ~~g = \sum_{i = 0}^{n} \frac{2 ^ {i \choose 2}}{i!}\times x^i~~, ~~f = \sum_{i = 1}^{n} \frac{2 ^ {i \choose 2}}{(i - 1)!}\times x^i~~
\]

所以有\(~f = h * g\), 但是我们要求的是\(~h_n~\),转换一下就是\(~h = g * f ^ {-1}\), 要用到多项式求逆

Code

#include<bits/stdc++.h>
#define For(i, j, k) for (int i = j; i <= k; ++i)
#define Forr(i, j, k) for (int i = j; i >= k; --i)
using namespace std;

inline void File() {
    freopen("bzoj3456.in", "r", stdin);
    freopen("bzoj3456.out", "w", stdout);
}

const int N = (1 << 20) + 10, mod = 1004535809;
int a[N], b[N], c[N], fac[N], inv[N], n;
int powg[N], invg[N], rev[N], siz, p[N], q[N];

inline int qpow(int a, int b) {
    static int res;
    for (res = 1; b; b >>= 1, a = 1ll * a * a % mod)
        if (b & 1) res = 1ll * res * a % mod;
    return res;
}

inline void Init(int n) {
    fac[0] = inv[0] = 1;
    For(i, 1, n) fac[i] = 1ll * i * fac[i - 1] % mod;

    inv[n] = qpow(fac[n], mod - 2);
    Forr(i, n - 1, 0) inv[i] = 1ll * (i + 1) * inv[i + 1] % mod;
}

inline int add(int x, int y) { return (x += y) >= mod ? x - mod : x; }

inline void Init_pow(int n) {
    int g = qpow(3, mod - 2);
    for (int i = 1; i <= n; i <<= 1) {
        powg[i] = qpow(3, (mod - 1) / i);
        invg[i] = qpow(g, (mod - 1) / i);
    }
}

inline void Init_rev(int n) {
    int bit = 0; for (siz = 1; siz <= n; siz <<= 1) ++ bit;
    For(i, 0, siz - 1) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}   

inline void NTT(int *a, int flag) {
    For(i, 0, siz - 1) if (rev[i] > i) swap(a[rev[i]], a[i]);   

    for (int i = 2; i <= siz; i <<= 1) {
        int wn = flag > 0 ? powg[i] : invg[i];

        for (int j = 0; j < siz; j += i) {
            int w = 1;
            for (int k = 0; k < i >> 1; w = 1ll * w * wn % mod, ++ k) {
                int x = a[j + k], y = 1ll * w * a[j + k + (i >> 1)] % mod;
                a[k + j] = add(x, y), a[k + j + (i >> 1)] = add(x, mod - y);
            }
        }
    }

    if (flag == -1) {
        int g = qpow(siz, mod - 2);
        For(i, 0, siz - 1) a[i] = 1ll * a[i] * g % mod;
    }
}

inline void Inv(int *a, int *b, int len) { // b is the inv of a
    if (len == 1) return (void) (b[0] = qpow(a[0], mod - 2));   

    Inv(a, b, len >> 1), Init_rev(len);
    For(i, 0, len - 1) p[i] = a[i], q[i] = b[i]; 

    NTT(p, 1), NTT(q, 1);
    For(i, 0, siz - 1) p[i] = 1ll * q[i] * q[i] % mod * p[i] % mod;
    NTT(p, -1);

    For(i, 0, len - 1) b[i] = add(2 * b[i] % mod, mod - p[i]);
}

int main() {
    File();
    Init(N - 5), Init_pow(1 << 20);

    scanf("%d", &n);
    For(i, 0, n) b[i] = 1ll * inv[i] * qpow(2, 1ll * (i - 1) * i / 2 % (mod - 1)) % mod;
    For(i, 1, n) c[i] = 1ll * inv[i - 1] * qpow(2, 1ll * (i - 1) * i / 2 % (mod - 1)) % mod;

    Init_rev(n); Inv(b, a, siz);    

    NTT(a, 1), NTT(c, 1);
    For(i, 0, siz - 1) a[i] = 1ll * a[i] * c[i] % mod;
    NTT(a, -1);

    int ans = 1ll * a[n] * fac[n - 1] % mod;
    printf("%d", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/LSTete/p/9575645.html

时间: 2024-08-30 08:10:06

【bzoj3456】城市规划(多项式求逆+dp)的相关文章

【bzoj3456】城市规划 容斥原理+NTT+多项式求逆

题目描述 求出n个点的简单(无重边无自环)无向连通图数目mod 1004535809(479 * 2 ^ 21 + 1). 输入 仅一行一个整数n(<=130000) 输出 仅一行一个整数, 为方案数 mod 1004535809. 样例输入 3 样例输出 4 题解 容斥原理+NTT+多项式求逆 设 $f_i$ 表示 $i$ 个点的简单无向连通图的数目,$g_i$ 表示 $i$ 个点的简单无向图的数目. 根据定义得 $g_i=2^{\frac{n(n-1}2}$ . 对于 $f_i$ ,考虑容斥

【BZOJ 3456】 3456: 城市规划 (NTT+多项式求逆)

3456: 城市规划 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 658  Solved: 364 Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一

多项式求逆 学习总结

感觉蒟蒻现在学多项式求逆貌似有些晚了 不过真的很有意思了(然而省选的时候自己还在玩泥巴什么也不会 多项式求逆是基于倍增的 假设我们知道 h(x)f(x)=1(mod x^n) 移项得 (h(x)*f(x)-1)=0(mod x^n) 两边同时求平方得 h(x)^2*f(x)^2 - 2*h(x)*f(x) +1 = 0 (mod x^2n) 设g(x)*f(x)=1(mod x^2n) 两边同时乘以g(x)可以得 h(x)^2*f(x) -2*h(x) + g(x) =0 (mod x^2n)

Luogu5162 WD与积木(生成函数+多项式求逆)

显然的做法是求出斯特林数,但没有什么优化空间. 考虑一种暴力dp,即设f[i]为i块积木的所有方案层数之和,g[i]为i块积木的方案数.转移时枚举第一层是哪些积木,于是有f[i]=g[i]+ΣC(i,j)·f[i-j],g[i]=ΣC(i,j)·g[i-j] (j=1~i). 考虑优化 .我们发现这个转移非常像卷积.写成卷积形式,有f[i]=g[i]+Σi!·Σf[i-j]/j!/(i-j)!,g[i]=i!·Σg[i-j]/j!/(i-j)!.直接分治NTT即可. 诶是不是强行多了个log?考

[Luogu5162]WD与积木(多项式求逆)

不要以为用上Stirling数就一定离正解更近,FFT都是从DP式本身出发的. 设f[i]为i个积木的所有方案的层数总和,g[i]为i个积木的方案数,则答案为$\frac{f[i]}{g[i]}$ 转移枚举第一层是哪些积木:$$f_n=g_n+\sum_{i=1}^{n}\binom{n}{i}f_{n-i},f_0=0$$$$g_n=\sum_{i=1}^{n}\binom{n}{i}g_{n-i},g_0=1$$ 转化成卷积形式:$$\frac{f_n}{n!}=\frac{g_n}{n!}

NTT+多项式求逆+多项式开方(BZOJ3625)

定义多项式h(x)的每一项系数hi,为i在c[1]~c[n]中的出现次数. 定义多项式f(x)的每一项系数fi,为权值为i的方案数. 通过简单的分析我们可以发现:f(x)=2/(sqrt(1-4h(x))+1) 于是我们需要多项式开方和多项式求逆. 多项式求逆: 求B(x),使得A(x)*B(x)=1 (mod x^m) 考虑倍增. 假设我们已知A(x)*B(x)=1 (mod x^m),要求C(x),使得A(x)*C(x)=1 (mod x^(2m)) 简单分析可得C(x)=B(x)*(2-A

Nescafe41 ProblemA 异化多肽 多项式求逆

题目大意:. 思路: 搞出C的生成函数F(x),那么: 长度为1的答案为F(x) 长度为2的答案为F2(x) - 故最终的答案为 F(x)+F2(x)+F3(x)+... =1?F+∞(x)1?F(x) =11?F(x) 然后就是多项式求逆了= = 跪picks大毒瘤 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M (263000&

【BZOJ】4555: [Tjoi2016&amp;Heoi2016]求和 排列组合+多项式求逆 或 斯特林数+NTT

[题意]给定n,求Σi=0~nΣj=1~i s(i,j)*2^j*j!,n<=10^5. [算法]生成函数+排列组合+多项式求逆 [题解]参考: [BZOJ4555][Tjoi2016&Heoi2016]求和-NTT-多项式求逆 $ans=\sum_{i=0}^{n}\sum_{j=0}^{i}s(i,j)*2^j*j!$ 令$g(n)=\sum_{j=0}^{n}s(n,j)*2^j*j!$ 则ans是Σg(i),只要计算出g(i)的生成函数就可以统计答案. g(n)可以理解为将n个数划分

多项式求逆

多项式求逆 求 \(A(x)\) 在 \(\%x^{n}\) 意义下的逆元 \(B(x)\) 首先求出 \(A(x)\) 在 \(\%x^{\lceil \frac{n}{2} \rceil}\) 意义下的逆元 \(C(x)\),即 $A(x)C(x)=1 $ \((\%x^{\lceil \frac{n}{2} \rceil})\) 移项得 \(A(x)C(x)-1 = 0\) \((\%x^{\lceil \frac{n}{2} \rceil})\) 两边平方 \(A^{2}(x)C^{2}