Description
众所周知,度度熊喜欢图,尤其是联通的图。
今天,它在图上又玩出了新花样,新高度。有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。
Input
第一行为T,表示输入数据组数。
对于每组数据,第一行三个整数N,M,K,表示N个点M条边的图。
接下来M行每行两个整数a,b,表示点a和点b之间有一条边。
1≤T≤20
1≤K≤N≤14
0≤M≤N∗(N+1)/2
1≤a,b≤N
Output
对第i组数据,输出
Case #i:
然后输出一行,仅包含一个整数,表示方法种数(对 1 000 000 009 取模) 。
Sample Input
3
1 0 1
1 1 1
1 1
3 3 2
1 2
2 3
1 3
Sample Output
Case #1:
1
Case #2:
2
Case #3:
3
Solution
状压,拿一个状态
i
表示一个点集,
dp[i][k]
表示点集
i
构成
f[i] 表示删掉 i 点集中若干边该点集还是一个连通块的方案数
枚举
dp[i][k]=∑jdp[i−j][k−1]⋅f[j]
,
j
为所有包含
对于
g[i] 为删掉 i 点集中若干边该点集超过一个连通块的方案数
令
类似的,考虑 i 点集中编号最小的点所处连通块,对该连通块中的边需要满足删掉后还是一个连通块,该连通块与其它点之间的边都删掉,剩下的边随便删,故有转移方程:
答案即为 dp[2N−1][K]
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn (1<<15)
#define mod 1000000009
int Case=1,T,n,m,K,a[15][15];
ll dp[maxn][15],cnt[maxn],f[maxn],g[maxn],b[111];
int Solve(int x)
{
int ans=0;
for(int i=0;i<n;i++)
if(x&(1<<i))
for(int j=i;j<n;j++)
if(x&(1<<j)&&a[i][j])ans++;
return ans;
}
int main()
{
b[0]=1;
for(int i=1;i<=105;i++)b[i]=b[i-1]*2%mod;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&K);
memset(a,0,sizeof(a));
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
u--,v--;
a[u][v]++;
if(u!=v)a[v][u]++;
}
int N=b[n];
for(int i=0;i<N;i++)cnt[i]=Solve(i);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(dp,0,sizeof(dp));
for(int i=1;i<N;i++)
{
int j=i;
while(j)
{
if(j&(i&-i))g[i]=(g[i]+b[cnt[i-j]]*f[j])%mod;
j=(j-1)&i;
}
f[i]=(b[cnt[i]]-g[i]+mod)%mod;
}
dp[0][0]=1;
for(int i=1;i<N;i++)
for(int k=1;k<=K;k++)
{
int j=i;
while(j)
{
if(j&(i&-i))dp[i][k]=(dp[i][k]+dp[i-j][k-1]*f[j])%mod;
j=(j-1)&i;
}
}
printf("Case #%d:\n%lld\n",Case++,dp[N-1][K]);
}
return 0;
}