您的位置:首页 > 其它

HDU 5713 K个联通块【状压计数dp……补集转化?

2016-12-16 00:02 381 查看
显然可以f[s][i] 表示点集s有i个连通块的方案数,枚举子集的时候,令其中一个的i=1,并强行把lowbit(s)表示的节点塞在i=1的子集里面,就避免了算重

然后考虑如何计算对于点集s 全部连通的方案数,发现好麻烦2333

转化一下 用选边的所有方案数 - 不连通的方案数

不连通的方案数……继续枚举子集,其中一个连通另一部分任选,并把lowbit(s)表示的节点放在联通的那个块里面

反正看代码就好……

明天磕主旋律,听说特别瘠薄233333

#include<bits/stdc++.h>
#define MAXN 16
#define MAXM 110
#define MOD 1000000009
using namespace std; int T,n,m,k;
int lim;

int G[MAXN][MAXN];

int cf[MAXM]; //2的次幂
int f[1<<MAXN][MAXN]; //f[s][i] 点集s有i个连通块的方案数
int num[1<<MAXN]; //点集s的边数
int g[1<<MAXN]; //点集s不连通的方案数

inline void init(){
memset(f,0,sizeof f);
memset(num,0,sizeof num);
memset(G,0,sizeof G);
memset(g,0,sizeof g);
}

inline int count_edge(int s,int pos){
for(int i=0;i<n;++i) if(pos==(1<<i)){
pos = i;break;
}

int rtn = 0;
for(int i=0;i<n;++i){
if((1<<i)&s)
rtn += G[pos][i];
}
return rtn;
}

int main(){
// freopen("1.in","r",stdin);

cf[0] = 1;
for(int i=1;i<MAXM;++i) cf[i] = ((0ll+cf[i-1])<<1)%MOD;
scanf("%d",&T);

int rondd = 0;
while(T--){
init();
scanf("%d%d%d",&n,&m,&k);
lim = (1<<n)-1;
for(int i=1;i<=m;++i){
int x,y;
scanf("%d%d",&x,&y);
--x,--y;
G[x][y] = G[y][x] = 1;
}
for(int i=1;i<=lim;++i){
int tmp = i&-i;
num[i] = num[i^tmp] + count_edge(i,tmp);
}

for(int i=0;i<n;++i) f[1<<i][1] = cf[num[1<<i]];

for(int i=1;i<=lim;++i){
int tmp = i&-i;
for(int j = i^tmp;j;j = (j-1)&(i^tmp))
(g[i] += 1ll* f[j^i][1] * cf[num[j]] % MOD) %= MOD;
f[i][1] = (cf[num[i]] - g[i]+MOD)%MOD;
}

for(int i=1;i<=lim;++i){
int tmp = i&-i;
for(int j = i^tmp;j;j = (j-1)&(i^tmp)){
for(int o = 2;o<=k;++o)
(f[i][o] += 1ll*f[j][o-1] * f[i^j][1]%MOD)%= MOD;
}
}
printf("Case #%d:\n%d\n",++rondd,f[lim][k]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: