题解[CF53E Dead Ends]
题目
不会翻译也不会概括
要说的话
最近一直在考试,没有什么时间来做讲课的题,分享的题,好不容易有时间做一道喜欢的题,好题。
浮云吹作雪,世味煮成茶。
Sol
注意到数据范围\(n=10\) ,马上经过反复考虑可以用状压。
设\(f[i][j]\)为:目前树的状态为\(i\) ,叶子节点的状态为\(j\)的方案数。
\(i\)的二进制第\(k\)位是\(1\)表示树上有第\(k\)个节点,\(j\)的二进制第\(k\)位是\(1\)表示。
所以,我们最好在连边的时候把每个点的编号-1,以便二进制存储。
状态转移时注意判断一下叶子节点就好了。
Code
#include<bits/stdc++.h>
#define ll long long
#define S (1025)
#define V (1024)
#define M (110)
#define N (11)
using namespace std;
struct xbk{int ed,nx;}e[M];
int n,m,k,cnt;
ll ans;
int head[N],num[S],f[S][S];
inline int read(){
int w=0;
char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9'){
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w;
}
inline void add(int a,int b){
e[++cnt].ed=b;
e[cnt].nx=head[a];
head[a]=cnt;
}
int main(){
n=read(),m=read(),k=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=V;i++)
for(int j=0;j<15;j++) if(i&(1<<j)) num[i]++;
//初始状态:树上只有一个节点,方案数为一
for(int i=1;i<(1<<n);i<<=1) f[i][i]=1;
for(int i=1;i<=m;i++){
int x=read()-1,y=read()-1;
add(x,y),add(y,x);
}
for(int i=1;i<(1<<n);i++){
for(int j=i;j;j--,j&=i){
//j:枚举叶子的状态
if(!f[i][j]) continue;
for(int k=0;k<n;k++){
//k:枚举树上的节点
if(!(i&(1<<k))) continue;
for(int l=head[k];~l;l=e[l].nx){
//l:枚举这个节点所连的边
int ed=e[l].ed,now=0;
//now:连边后叶子的状态
if(i&(1<<ed)) continue;
//如果树上已经有这个点了,就跳过
if(num[i]==1) now=i|(1<<ed);
//如果树上目前只有一个点,那连边之后两个点都是叶子
else now=(j&(~(1<<k)))|(1<<ed);
//否则,ed一定是叶子,k如果原来是叶子,那现在就不是叶子
if(!(now>>(ed+1))) f[i|(1<<ed)][now]+=f[i][j];
//更新状态
}
}
}
}
for(int i=0;i<(1<<n);i++)
if(num[i]==k) ans+=f[(1<<n)-1][i];
//统计答案
printf("%lld\n",ans);
return 0;
}