HDU 5713 K個聯通塊 狀壓dp列舉子集 (2016百度之星複賽)
阿新 • • 發佈:2019-02-05
題意
眾所周知,度度熊喜歡圖,尤其是聯通的圖。
今天,它在圖上又玩出了新花樣,新高度。有一張無重邊的無向圖, 求有多少個邊集,使得刪掉邊集裡的邊後,圖裡恰好有K個連通塊。
推薦題目:Codeforces #332 div 2 E Nuts (599E) 狀壓+樹形+計數
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <queue> #include <vector> #include <iostream> #include <map> #define pii pair<int,int> #define mp(a,b) make_pair<a,b> #define xx first #define yy second #define mem(a) memset( a, 0, sizeof(a) ) using namespace std; typedef long long ll; ll dp[20][1<<15], f[1<<15], g[1<<15], f2[1000]; int fa[20], vis[1<<15]; int w[1<<15], n, m, k; const ll mod = 1e9+9; /* dp[i][st]存的是現在有i個連通塊,加入的點的情況是st f[st]存的是這個連通塊有多少個作為連通塊的形態數(就是這個連通塊包含的邊怎麼個刪法使他還是個連通塊,有多少種情況) g[st]輔助陣列 抓住 第一個點一定屬於一個連通分量(所以通俗來說列舉子集時只需要列舉一半即可) */ int root( int x ) { if( fa[x] == x ) return x; else return root( fa[x] ); } void merge( int a, int b ) { if( root(a) == root(b) ) return ; int pa, pb; pa = root(a); pb = root(b); fa[pa] = pb; return ; } int lowbit( int x ) { return x&-x; } void solve() { int i, j, st; cin >> n >> m >> k; mem(f);mem(g);mem(vis); for( i = 0; i < n; i ++ )fa[i] = i; for( i = 1; i <= m; i ++ ){ int x, y; scanf("%d %d", &x, &y); x --; y -- ; for( j = 0; j < (1<<n); j ++ ){ if( ( j&(1<<x) ) && ( j&(1<<y) ) ){ f[j] ++; } } merge( x, y ); }// 用並查集判連通 for( st = 1; st < (1<<n); st ++ ){ int p = lowbit(st), to = -1, fail = 0; for( i = 0; i < n; i ++ ){ if( st & ( 1 << i ) ){ if( to == -1 ){ to = root(i); } else{ if( root(i) != to ){ fail = 1; break; } } } } if( fail ){ continue; } else vis[st] = 1; f[st] = f2[f[st]]; g[st] = f[st]; for( j = st; j ; j = (j-1) & st ){ if( j == st ) continue; if( !vis[j] ) continue; if( j & p ){ f[st] -= ((ll)f[j]*g[j^st])%mod; f[st] %= mod; } } }// vis陣列判這個子集本身是否連通,不聯通f g = 0(可以不用判斷) memset( dp, 0, sizeof(dp) ); dp[0][0] = 1; for( i = 1; i <= k; i ++ ){ for( st = 1; st < (1<<n); st ++ ){ int p = lowbit(st), sub; for( sub = st; sub; sub = ( sub - 1 )&st ){ if( !vis[sub] ) continue; if( p & sub ){ dp[i][st] += ( (ll)dp[i-1][sub^st]*f[sub] )%mod; dp[i][st] %= mod; } } } } ll ans = (dp[k][(1<<n)-1]+mod)%mod; printf("%lld\n", ans); } void init() { f2[0] = 1; for( ll i = 1; i <= 999; i ++ ){ f2[i] = f2[i-1]*2%mod; } } int main() { int cas = 1, T; init(); cin >> T; while( T -- ){ printf("Case #%d:\n", cas ++); solve(); } }