题解[CF53E Dead Ends]

题目

CF

Luogu

不会翻译也不会概括

要说的话

最近一直在考试,没有什么时间来做讲课的题,分享的题,好不容易有时间做一道喜欢的题,好题。

浮云吹作雪,世味煮成茶。

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;
}

完结撒花❀

posted @ 2021-03-16 21:17  xxbbkk  阅读(103)  评论(0编辑  收藏  举报