zoukankan      html  css  js  c++  java
  • CF848D Shake It!

    CF848D Shake It!

    给定 (n,m),现在按照此规则生成无向图,初始我们有一张图 (G),仅包含两个节点 (s,t),以及连接 (s o t) 的边。

    每次你可以选择一条已经存在的边 ((u,v)),然后将往图中加入节点 (w) 和边 ((u,w),(w,v)),这样操作 (n) 次,我们将得到一张图 (G')

    定义 (G') 合法,当且仅当 (min cut(s,t)=m),即 (s o t) 的最小割为 (m)

    求本质不同的合法的图 (G') 的数量,答案对 (10^9+7) 取模。

    其中,两张图 (G_1,G_2) 不同,当且仅当不存在一种作用于点集的置换,使得边对应相同,同时 (s,t) 对应相同。

    (n,mle 50)

    translated by EmptySoulist

    Solution

    方便起见,我们认为一次给 ((u,v)) 加入 (w) 的操作为 "加点/添加节点" 操作,我们使用 ((u o w o v)) 来描述此操作。

    同时,不难通过观察发现加点有且仅有两种方向(Left and Right)。

    请注意,此图上允许对一条边重复添加节点,这非常重要。

    我们不难观察到这张图一定是平面图,平面图最小割等于对偶图最短路。

    如果不考虑给一条边重复添加节点的情况,我们发现增加节点的过程在对偶图上非常类似于树的形态,考虑一棵树,儿子有 (0,1) 两种颜色( (0) 表示从左边加,(1) 表示右边加)每次增加节点的时候我们给其赋予一个颜色即可。

    不难发现此时对偶图最短路即这棵树深度最小的儿子的深度。

    接着我们再考虑,允许重复添加节点的情况,我们假设 ((u o v)) 的边被加入了两次 ((u o w_1 o v,u o w_2 o v)),同时 (u o w_1) 等边也被加入过点。

    我们发现这张平面图上这些边的 内外 顺序是可以任意调控的,这间接使得我们想到,答案应该是只考虑 (u o w_1 o v) 的边存在的情况和 (u o w_2 o v) 存在的情况下答案的和。

    通过简单的画图也可以说明这件事。下面将给出一个例子:

    第二张图对应的对偶图形如如此:

    (保留了 (s o t) 的边方便观看)

    其中 (A o G) 的最短路即为答案。

    进一步简化,两张图对应的对偶图分别为:

    第二张图删去了与 (G) 的连边。

    注意到这两张图本质上是相同的,也出于进一步的观察,我们不难发现我们可以对真实的对偶图进行一步简化,在初始建图中,假设某条边被操作了 (k) 次(且方向相同)我们可以类似于没有边被重复操作时的例子,直接建成树的形态,将这 (k) 条边同时挂在 (u o v) 这条边对应的点之下,假设这些边的方向均为 Left,我们发现答案即为这些独立树的答案和!

    证明的话画一下图就知道了(具体论证有点麻烦,我不太方便画图 TAT)。

    假设 Left 为颜色 (0),Right 为颜色 (1),那么我们不难得到对一棵简化的树计算答案的方法:

    [f_x=min(sum_{x o v,vin extrm{left}} f_v,sum_{x o v,vin extrm{right}} f_v)+1 ]

    由于初始根没有方向区分,所以我们只能将这张图建成森林,其贡献即为各棵子树根节点的 (f) 之和 (+1)

    我们发现本质不同的图,等价于这样简化后的树不同。

    那么考虑对树进行计数,我们设 (g_{i,j}) 表示 (i) 个节点的树,其根节点 (f) 值为 (j) 的树的方案数。

    我们发现本质不同其实说明儿子任意交换对答案没有影响,那么这里类似于无排列规则的计数问题。比如两棵树形如 (1,2,2),那么 (212,122,221) 均被视为相同的排列。

    于是我们对 left 和 right 分别进行转移,然后通过一次类似于卷积的形式合并答案即可。

    其中,left 和 right 在转移的过程使用的数组相同,我们发现问题的核心点在于如何去重上了,我们使用 (f) 数组来表示我们需要的答案。

    假设从小到大加入元素,此时 (i) 是我们最后加入的元素,对于 (i,j) 不同的元素,我们从小到大枚举 (j),这样我们保证了 ((i,j)) 的对子是按照 (i) 为第一关键字,(j) 为第二关键字从小到大加入集合的。

    对于 ((i,j)) 相同的对子,我们发现问题等价于有 (g_{i,j}) 个数,每个数出现次数任意,如果所有数出现次数相同那么算同一种方案,求方案数。

    假设 (g_{i,j}) 被使用了 (k) 次,那么不难发现贡献即 (inom{g_{i,j}+k-1}{k}),即 (frac{1}{(1-x)^k}[x^{g_{i,j}}]),是经典模型,这样的话我们需要枚举 (k)

    每次转移得到 (f) 之后,我们不难转移 (g)

    (g_{i,j}leftarrow sum_{u+v=i-1,min(l,r)=j-1}f_{u,l}f_{v,r})

    这样我们一边递推 (g),一遍递推 (f) 即可。

    边界为 (g_{1,1}=1),同时不难发现我们需要的答案即为 (f_{n,m-1})

    复杂度为 (mathcal O(n^4ln n))

    枚举顺序上有一些坑,建议多想想后写。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std ;
    #define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
    #define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
    #define re register
    #define int long long
    int gi() {
    	char cc = getchar() ; int cn = 0, flus = 1 ;
    	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
    	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
    	return cn * flus ;
    }
    const int P = 1e9 + 7 ; 
    const int N = 50 + 5 ; 
    int n, m, g[N][N], f[N][N], fac[N], inv[N], D[N][N][N] ;
    int fpow(int x, int k) {
    	int ans = 1, base = x ;
    	while(k) {
    		if(k & 1) ans = ans * base % P ;
    		base = base * base % P, k >>= 1 ;
    	} return ans ;
    }
    void inc(int &x, int y) {
    	((x += y) >= P) && (x -= P) ; 
    }
    int C(int x, int y) {
    	int ans = 1 ;
    	rep( i, 1, y ) ans = ans * (x - i + 1) % P ; 
    	return ans * inv[y] % P ; 
    }
    signed main()
    {
    	n = gi(), m = gi() ; fac[0] = inv[0] = 1 ; 
    	rep( i, 1, n ) fac[i] = fac[i - 1] * i % P, inv[i] = fpow( fac[i], P - 2 ) ; 
    	g[0][0] = f[0][0] = 1 ; 
    	for(re int i = 1; i <= n; ++ i) {
    		for(re int u = 0; u < i; ++ u) { 
    			int v = i - u - 1 ; 
    			rep( l, 0, u ) rep( r, 0, v )
    				inc( g[i][min(l, r) + 1], f[u][l] * f[v][r] % P ) ; 
    		}
    		rep( k, 1, n / i ) rep( j, 1, i ) D[k][i][j] = C(g[i][j] + k - 1, k) ; 
    		rep( j, 1, i ) drep( x, i, n ) rep( k, 1, x / i )  {
    			int d = D[k][i][j] ; 
    			rep( li, k * j, x ) inc( f[x][li], f[x - i * k][li - k * j] * d % P ) ;
    		}
    	}
    	cout << f[n][m - 1] << endl ; 
    	return 0 ;
    }
    
  • 相关阅读:
    无限维
    黎曼流形
    why we need virtual key word
    TOJ 4119 Split Equally
    TOJ 4003 Next Permutation
    TOJ 4002 Palindrome Generator
    TOJ 2749 Absent Substrings
    TOJ 2641 Gene
    TOJ 2861 Octal Fractions
    TOJ 4394 Rebuild Road
  • 原文地址:https://www.cnblogs.com/Soulist/p/13747265.html
Copyright © 2011-2022 走看看