zoukankan      html  css  js  c++  java
  • 【bzoj3456】城市规划(多项式求逆+dp)

    Description

    (~n~)个点组成的有标号无向连通图的个数。(~1 leq n leq 13 imes 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 imes {{i - 1} choose {j - 1}}} imes 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 imes frac{(i - 1)!}{(j - 1)!(i - j)! } imes {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 imes {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)!} imes 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)!} imes x^i~~, ~~g = sum_{i = 0}^{n} frac{2 ^ {i choose 2}}{i!} imes x^i~~, ~~f = sum_{i = 1}^{n} frac{2 ^ {i choose 2}}{(i - 1)!} imes 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;
    }
    
    
  • 相关阅读:
    测试 多线程 实现 callable 带返回值
    给定一个 hashMap 最终输出最大值的键
    正则判断输入的字符(英文、数字、空格、其他)的个数
    当返回值为json字符串时 如何获得其中的json数组
    thread run 和 start 的区别
    docker 构建dockerfile
    jsonp 跨域
    springsession 实现session 共享
    通过反射获得 spring 的 RequestMapping value值
    redis 集群搭建 以及 报错解决
  • 原文地址:https://www.cnblogs.com/LSTete/p/9575645.html
Copyright © 2011-2022 走看看