【HDOJ5713】K个联通块(状压DP,计数)
题意:有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。
1≤T≤20
1≤K≤N≤14
0≤M≤N∗(N+1)/2
1≤a,b≤N
思路:From http://blog.csdn.net/di4covery/article/details/51699544
一个简单的结论:删除边集的个数 = 选择边集的个数 , 这样我们就可以把问题转化为选取若干条边使得图里面恰好有K个联通块。
问题可以转化成三个状态压缩DP来完成(这三个DP都不难)
1、num[ mask ] 表示选取mask状态的点两两连边的边数。(这是一个很小的数)
这个数组我们通过状态压缩DP来求
令 u = lowbit (mask) (即mask状态中编号最小的那个点)
枚举mask中的点v,计算边u,v的个数tmp
num [ mask ] = num [ mask ^u ] + tmp;
2、f [ mask ] 表示选取mask状态的点 ,构成一个联通块 的方案数
同样,利用上一次求出来的num数组,状态压缩DP来求
容斥原理 : 合法方案数 = 总方案数 - 不合法方案数
总方案数 : 2 ^ num( mask ) (每条边选或者不选)
不合法方案数 = tmp:
令 u = lowbit (mask)
枚举mask一个不包含u的子集s,tmp = tmp + f(mask ^ s) * ( 2 ^ num[ s ] );
上面的式子可以理解为枚举一个包含u的联通块,然后剩下的点两两之间的边选或者不选,这些边不会连接之前的联通块
f [ mask ] = 2 ^ num( mask) - tmp;
3、F[ mask ] [ n ]表示选取mask状态的点,构成n个联通块的方案数
实际上 f[ mask ]是 F[mask][1] ,剩下的状态压缩DP相对简单
枚举mask,枚举n
令u = lowbit( mask ),枚举mask一个不包含u的子集s
F[ mask ][ n ] = F[ s ][ n-1 ] * F[ mask ^ s ][ 1 ]
上面的式子等价于F[ mask ][ n ] = F [ s ][ n-1 ] * f[ mask ^ s ]
答案即为F[ mask ] [ n ] (mask为所有点都选择的状态,就是2^n - 1)
Pascal对拍已A,卡常没办法,估计如果去年现场做要跪(现在也跪)
1 const mo=1000000009; 2 var g:array[0..35000,0..15]of int64; 3 f:array[0..35000]of int64; 4 two:array[0..200]of int64; 5 num:array[0..35000]of longint; 6 a:array[0..20,0..20]of longint; 7 cas,v,i,t,x,y,sta,max,k1,n,m,j:longint; 8 tmp:int64; 9 10 function lowbit(x:longint):longint; 11 begin 12 exit(x and (-x)); 13 end; 14 15 begin 16 assign(input,'hdoj5713.in'); reset(input); 17 assign(output,'hdoj5713.out'); rewrite(output); 18 readln(cas); 19 two[0]:=1; 20 for i:=1 to 200 do two[i]:=two[i-1]*2 mod mo; 21 for v:=1 to cas do 22 begin 23 readln(n,m,k1); 24 25 26 max:=(1<<n)-1; 27 for i:=1 to n do 28 for j:=1 to n do a[i,j]:=0; 29 for i:=1 to max do 30 begin 31 num[i]:=0; f[i]:=0; 32 end; 33 for i:=1 to max do 34 for j:=1 to n do g[i,j]:=0; 35 for i:=1 to m do 36 begin 37 read(x,y); 38 a[x,y]:=1; a[y,x]:=1; 39 end; 40 for i:=1 to max do 41 begin 42 x:=lowbit(i); y:=round(ln(x)/ln(2))+1; 43 num[i]:=num[i xor x]; 44 for j:=1 to n do 45 if i and (1<<(j-1))>0 then num[i]:=(num[i]+a[y,j]) mod mo; 46 end; 47 for sta:=1 to max do 48 begin 49 x:=lowbit(sta); 50 t:=sta xor x; y:=t; tmp:=0; 51 while t>0 do 52 begin 53 tmp:=tmp+f[sta xor t]*two[num[t]]; 54 tmp:=tmp mod mo; 55 t:=y and (t-1); 56 end; 57 f[sta]:=(two[num[sta]]-tmp) mod mo; 58 end; 59 for i:=1 to max do g[i,1]:=f[i]; 60 for sta:=1 to max do 61 for i:=2 to k1 do 62 begin 63 x:=lowbit(sta); 64 t:=sta xor x; y:=t; 65 while t>0 do 66 begin 67 g[sta,i]:=g[sta,i]+g[t,i-1]*f[sta xor t]; 68 g[sta,i]:=g[sta,i] mod mo; 69 t:=y and (t-1); 70 end; 71 72 73 end; 74 75 76 writeln('Case #',v,':'); 77 writeln(g[max,k1]); 78 end; 79 80 close(input); 81 close(output); 82 end.
对拍用的C++ 1000+MS
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #define MAXMASK 65537 5 #define N 20 6 #define mod 1000000009LL 7 8 using namespace std; 9 10 int e[N][N],n,m,k,MASK,num[MAXMASK]; 11 long long F[MAXMASK][N],f[MAXMASK]; 12 13 inline int lowbit(int x){return x & (-x);}; 14 15 int edge(int s, int p) { 16 int ans = 0; 17 for (int i = 1; i <= n; i++) 18 if ((1<<(i-1)) == p) {p = i;break;} 19 for (int i = 1; i <= n; i++) if ((1<<(i-1))&s) ans += e[i][p]; 20 return ans; 21 } 22 23 int main() 24 { 25 freopen("hdoj5713.in","r",stdin); 26 freopen("right.out","w",stdout); 27 int T = 0; 28 scanf("%d",&T); 29 for (int rank=1;rank<=T;rank++) 30 { 31 memset(e,0,sizeof(e)); 32 memset(F,0,sizeof(F)); 33 memset(f,0,sizeof(f)); 34 memset(num,0,sizeof(num)); 35 scanf("%d%d%d",&n,&m,&k); 36 MASK = (1 << n) - 1; 37 for (int i=1;i<=m;i++) { 38 int a,b; 39 scanf("%d%d",&a,&b); 40 e[a][b] = e[b][a] = 1;//顺带处理自环:) 41 } 42 //计算mask状态下的边数 43 for (int i=1;i<=MASK;i++) { 44 int t = lowbit(i); 45 num[i] = num[i^t] + edge(i,t);//往状态里新增加一个点 46 } 47 48 //状态压缩DP求小f 49 50 for (int s=1;s<=MASK;s++){ 51 int t = lowbit(s); 52 long long tmp = 0LL; 53 for (int i=(s^t);i>0;i=((i-1)&(s^t))) { 54 tmp += f[s^i] * 1LL*(1LL << num[i]); 55 tmp %= mod; 56 } 57 f[s] = (1LL*(1LL<<num[s]) - tmp) % mod; 58 } 59 // for (int s=1;s<=MASK;s++) printf("%d\n",f[s]); 60 //小f为大F的第一项 61 for (int s=1;s<=MASK;s++) F[s][1] = f[s]; 62 63 //状态压缩DP求大F 64 for (int s=1;s<=MASK;s++) 65 for (int i=2;i<=k;i++) { 66 int t = lowbit(s); 67 for (int j=s^t;j>0;j=(j-1)&(s^t)) {//枚举s的子集 68 F[s][i] += F[j][i-1] * f[s^j]; 69 F[s][i] %= mod; 70 } 71 } 72 73 //防止答案小于0 74 while (F[MASK][k] < 0) F[MASK][k] += mod; 75 printf("Case #%d:\n%I64d\n", rank, F[MASK][k]); 76 } 77 return 0; 78 }