# HDU 5713 K个联通块 状压dp枚举子集 （2016百度之星复赛）

#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();
}
}


• 本文已收录于以下专栏：

## UVA 11825 - Hackers' Crackdown 状态压缩 dp 枚举子集

11825 - Hackers' Crackdown 状态压缩 dp 枚举子集 ACM 题目地址：11825 - Hackers' Crackdown 题意：  有一个由编号0~n-1的n台计算...
• hcbbt
• 2014-06-27 21:35
• 1465

## HDU Untitled（状压DP OR dfs枚举子集）

Untitled Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total...

## hdu-5823-color II-状压DP-枚举子集的子集

http://acm.hdu.edu.cn/showproblem.php?pid=5823 给你一个无向图,然后要枚举所有点的子集的最小染色方案数,然后输出 ∑ans[i]*233^i m...

## 2016百度之星复赛 1003 拍照 扫描线

举报原因： 您举报文章：深度学习：神经网络中的前向传播和反向传播算法推导 色情 政治 抄袭 广告 招聘 骂人 其他 (最多只允许输入30个字)