zoukankan      html  css  js  c++  java
  • 有标号DAG计数 [容斥原理 子集反演 组合数学 fft]

    有标号DAG计数

    题目在COGS上

    [HZOI 2015]有标号的DAG计数 I

    [HZOI 2015] 有标号的DAG计数 II

    [HZOI 2015]有标号的DAG计数 III


    I

    求n个点的DAG(可以不连通)的个数。(n le 5000)

    2013年王迪的论文很详细了

    感觉想法很神,自己怎么想到啊?


    首先要注意到DAG中一类特殊的点:入度为0的点。以这些点来分类统计


    先是一种(O(N^3))的dp, (d(i,j)) i个点j个入度为0,转移枚举去掉j个后入度为0点的个数,乘上连边情况


    在弱化条件时特殊化

    (f(n, S)) n个点,只有S中的点入度为0

    (g(n,S)) n个点,至少S中的点入度为0

    [g(n, S) = 2^{mid smid (n-mid Smid)} g(n-mid Smid, varnothing) \ g(n, S) = sum_{S subset T} f(n, T)quad (1) ]

    子集反演!

    [f(n, S) = sum_{S subset T} (-1)^{mid Tmid - mid Smid} g(n, S)quad (2) ]

    我们目标是求(g(n, varnothing)),

    代入((1),(2)),然后使用枚举集合大小,乘上组合数的技巧,(m = mid Tmid, k = mid Smid)。还需要用(inom{n}{k} inom{k}{m} = inom{n}{m} inom{n-m} {k-m})替换,最后得到

    [g(n, varnothing) = sum_{k=1}^n (-1)^{k-1} inom{n}{k} 2^{k(n-k)} g(n-k, varnothing) ]

    完成!


    现在尝试直接考虑这个式子的意义:

    [n个点DAG个数= ge 1 个入度为0 - ge 2个入度为0 + ge 3.... ]



    II

    求n个点的DAG(可以不连通)的个数。(n le 10^5)

    当然要用fft啦!分治或者多项式求逆。

    (2^{k(n-k)})怎么办?

    和hdu那道题类似,把((n-k)^2 = n^2 - 2nk + k^2)代入

    但这样会出现(2^{frac{n}{2}}), 2的逆元(mod P-1)不存在,所以要求2的二次剩余(x^2 equiv 2 pmod {P-1})



    III & IIII

    求n个点的DAG(必须连通)的个数。(n le 5000, n le 10^5)

    城市规划类似的思想...

    可以不连通 到 连通



    Code

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N = 5005, P = 10007;
    inline int read() {
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int n, g[N];
    int inv[N], fac[N], facInv[N], mi[P+5];
    inline int C(int n, int m) {return fac[n] * facInv[m] %P * facInv[n-m] %P;}
    int main() {
    	freopen("DAG.in", "r", stdin);
    	freopen("DAG.out", "w", stdout);
    	//freopen("in", "r", stdin);
    	n = read();
    	inv[1] = fac[0] = facInv[0] = 1;
    	for(int i=1; i<=n; i++) {
    		if(i != 1) inv[i] = (P - P/i) * inv[P%i] %P;
    		fac[i] = fac[i-1] * i %P;
    		facInv[i] = facInv[i-1] * inv[i] %P;
    	}
    	mi[0] = 1;
    	for(int i=1; i<=P; i++) mi[i] = mi[i-1] * 2 %P;
    	g[0] = 1;
    	for(int i=1; i<=n; i++) 
    		for(int k=1; k<=i; k++) g[i] = (g[i] + ((k&1) ? 1 : -1) * C(i, k) %P * mi[k * (i-k) % (P-1)] %P * g[i-k] %P) %P;
    	printf("%d
    ", (g[n] + P) %P);
    }
    
    

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N = (1<<18) + 5, P = 998244353, qr2 = 116195171;
    inline int read() {
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    ll Pow(ll a, int b) {
    	ll ans = 1;
    	for(; b; b >>= 1, a = a * a %P)
    		if(b & 1) ans = ans * a %P;
    	return ans;
    }
    
    namespace fft {
    	int rev[N];
    	void dft(int *a, int n, int flag) { 
    		int k = 0; while((1<<k) < n) k++;
    		for(int i=0; i<n; i++) {
    			rev[i] = (rev[i>>1]>>1) | ((i&1)<<(k-1));
    			if(i < rev[i]) swap(a[i], a[rev[i]]);
    		}
    		for(int l=2; l<=n; l<<=1) {
    			int m = l>>1;
    			ll wn = Pow(3, flag == 1 ? (P-1)/l : P-1-(P-1)/l);
    			for(int *p = a; p != a+n; p += l) 
    				for(int k=0, w=1; k<m; k++, w = w*wn%P) {
    					int t = (ll) w * p[k+m] %P;
    					p[k+m] = (p[k] - t + P) %P;
    					p[k] = (p[k] + t) %P;
    				}
    		}
    		if(flag == -1) {
    			ll inv = Pow(n, P-2);
    			for(int i=0; i<n; i++) a[i] = a[i] * inv %P;
    		}
    	}
    	int t[N];
    	void inverse(int *a, int *b, int l) {
    		if(l == 1) {b[0] = Pow(a[0], P-2); return;}
    		inverse(a, b, l>>1);
    		int n = l<<1;
    		for(int i=0; i<l; i++) t[i] = a[i], t[i+l] = 0; 
    		dft(t, n, 1); dft(b, n, 1);
    		for(int i=0; i<n; i++) b[i] = (ll) b[i] * (2 - (ll) t[i] * b[i] %P + P) %P;
    		dft(b, n, -1); for(int i=l; i<n; i++) b[i] = 0;
    	}
    }
    
    int n, a[N], b[N], len;
    ll inv[N], fac[N], facInv[N];
    int main() {
    	//freopen("in", "r", stdin);
    	freopen("dag_count.in", "r", stdin);
    	freopen("dag_count.out", "w", stdout);
    	n = read();
    	inv[1] = fac[0] = facInv[0] = 1;
    	for(int i=1; i<=n; i++) {
    		if(i != 1) inv[i] = (P - P/i) * inv[P%i] %P;
    		fac[i] = fac[i-1] * i %P;
    		facInv[i] = facInv[i-1] * inv[i] %P;
    	}
    	for(int i=1; i<=n; i++) {
    		int t = facInv[i] * Pow(Pow(qr2, (ll) i * i %(P-1)), P-2) %P;
    		if(i&1) b[i] = P - t; else b[i] = t;
    	} 
    	b[0] = (b[0] + 1) %P;
    	len = 1; while(len <= n) len <<= 1;
    	fft::inverse(b, a, len);
    	int ans = (ll) a[n] * fac[n] %P * Pow(qr2, (ll) n * n %(P-1)) %P;
    	printf("%d
    ", ans);
    }
    
    

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    const int N = 5005, P = 10007;
    inline int read() {
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    int n, g[N], f[N];
    int inv[N], fac[N], facInv[N], mi[P+5];
    inline int C(int n, int m) {return fac[n] * facInv[m] %P * facInv[n-m] %P;}
    int main() {
    	freopen("DAGIII.in", "r", stdin);
    	freopen("DAGIII.out", "w", stdout);
    	//freopen("in", "r", stdin);
    	n = read();
    	inv[1] = fac[0] = facInv[0] = 1;
    	for(int i=1; i<=n; i++) {
    		if(i != 1) inv[i] = (P - P/i) * inv[P%i] %P;
    		fac[i] = fac[i-1] * i %P;
    		facInv[i] = facInv[i-1] * inv[i] %P;
    	}
    	mi[0] = 1;
    	for(int i=1; i<=P; i++) mi[i] = mi[i-1] * 2 %P;
    	g[0] = 1;
    	for(int i=1; i<=n; i++) 
    		for(int k=1; k<=i; k++) 
    			g[i] = (g[i] + ((k&1) ? 1 : -1) * C(i, k) %P * mi[k * (i-k) % (P-1)] %P * g[i-k] %P) %P;
    
    	f[0] = 1;
    	for(int i=1; i<=n; i++) {
    		f[i] = g[i];
    		for(int j=1; j<i; j++) f[i] = (f[i] - (ll) C(i-1, j-1) * f[j] %P * g[i-j]) %P;
    		if(f[i] < 0) f[i] += P;
    	}
    	printf("%d
    ", (f[n] + P) %P);
    }
    
    
    
  • 相关阅读:
    远程桌面无法复制粘贴
    软件无法启动,不提示具体错误
    从数据库统计出某张表中某个字段重复次数
    程序员何去何从
    SQL Server中TRUNCATE 和 DELETE的区别
    关于C#自定义控件注释说明
    C#的winform程序下如何实现文本编辑框(TextBox)的Hint提示文字效果
    ubuntu固定内网ip地址
    数据库的优化处理 Memory cached
    MYSQL管理之主从同步管理
  • 原文地址:https://www.cnblogs.com/candy99/p/6803263.html
Copyright © 2011-2022 走看看