题意:
解法:
n<=10,容易想到状压。
d[s][t]表示点集s连接成一棵生成树,其中点集t是end point的方案数。
对于集合d[s][t],枚举集合s内的点i,枚举集合s外的点j,
则d[nt_s][nt_t]+=d[s][t].
d[s][t]状态包含多种生成树,
对于其中的同一种生成树,根据加边顺序的不同,有不同的构造顺序,
这题我们应该忽略加边的顺序,所以对于每个d[s][t],需要首先将d[s][t]/=cnt[t]
这里cnt[t]表示t中二进制1的个数,
因为对于一棵生成树,可以由cnt[t]种生成树转移得来(考虑删掉一个end_point,有cnt[t]种删法),
由于最终加的边是一样的,所以对于这一棵树,方案重复累加了cnt[t]次。
Code:
#include <bits/stdc++.h>
using namespace std;
#define X first
#define Y second
#define int long long
#define PI pair<int, int>
const int maxm = 2e5 + 5;
int g[11][11];
int d[1111][1111];
int cnt[1111];
int n,m,k;
void init() {
for(int t=0;t<1111;t++){
cnt[t]=cnt[t>>1]+(t&1);
}
}
void solve() {
init();
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
int x,y;cin>>x>>y;
x--,y--;
g[x][y]=1;
d[(1<<x)|(1<<y)][(1<<x)|(1<<y)]++;
g[y][x]=1;
d[(1<<x)|(1<<y)][(1<<x)|(1<<y)]++;
}
for(int s=0;s<(1<<n);s++){
for(int t=0;t<(1<<n);t++){
if((s&t)!=t)continue;
if(!d[s][t])continue;
d[s][t]/=cnt[t];
for(int i=0;i<n;i++){
int i_s=(s>>i&1);
int i_t=(t>>i&1);
if(!i_s)continue;
for(int j=0;j<n;j++){
if(!g[i][j])continue;
int j_s=(s>>j&1);
int j_t=(t>>j&1);
if(j_s)continue;
int nt_s=s|(1<<j);
int nt_t=t;
if(i_t)nt_t^=(1<<i);
nt_t|=(1<<j);
d[nt_s][nt_t]+=d[s][t];
}
}
}
}
int ans=0;
for(int t=0;t<(1<<n);t++){
if(cnt[t]==k){
ans+=d[(1<<n)-1][t];
}
}
cout<<ans<<endl;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
#ifndef ONLINE_JUDGE
freopen("../in.txt", "r", stdin);
freopen("../out.txt", "w", stdout);
#endif
#ifdef MULTI_CASE
int T;
cin >> T;
while (T--)
#endif
solve();
return 0;
}