一本通 高手训练 1781 死亡之树 状态压缩dp

LINK:死亡之树

关于去重 还是有讲究的。

题目求本质不同的 具有k个叶子节点的树的个数 不能上矩阵树。

点数很少容易想到装压dp 考虑如何刻画树的形状 发现一个维度做不了 所以。

设状态 f[i][j]表示 点的集合为i叶子集合的点为j的方案树。

这样我们就能知道这棵树大致的样子 空间 为\(2^{2n}\)

当然 如果使用三进制状压 空间复杂度还会进一步降到3^n 不过这道题没有卡空间。

考虑转移 可以枚举点 也可以枚举边。

先考虑枚举边 会出现重复的情况 如先加这条边再加那条边 两条边可以交换。

考虑强制按顺序的话 需要记上次加入的边是什么。时间上过不了。

考虑最后除以(n-1)! 这样也不行因为 这样的话 每次考虑加入边的时候点的状态的刻画存在问题。

考虑枚举点 此时实质还是在加边 还是有重复的。

甚至在某个时刻都有可能出现顺序带来方案数不同的问题。

还是考虑点的有序性 考虑 从一个状态到达另外一个状态转移的唯一性。

可以发现在枚举决策的时候 只有当前决策大于叶子节点的最大的那个再进行转移 此时这样就保证了每一种树都是以唯一的方式构造出来的。

证明 倒着想 考虑当前的一棵树的上一个状态 一定是当前状态减掉最大编号的叶子节点得到的 如果不是 那么上个状态是不能转移到当前状态的。

归纳一下可以得证。

const ll MAXN=11;
ll n,maxx,m,K;
ll sum[1<<MAXN];
ll f[1<<MAXN][1<<MAXN];
ll a[MAXN][MAXN];
signed main()
{
	freopen("dead.in","r",stdin);
    freopen("dead.out","w",stdout);
	get(n);get(m);get(K);
	rep(1,m,i)
	{
		ll get(x);ll get(y);
		a[x][y]=a[y][x]=1;
	}
	rep(2,n,i)if(a[1][i])f[1|(1<<(i-1))][1|(1<<(i-1))]=1;
	maxx=(1<<n)-1;
	rep(1,maxx,i)sum[i]=sum[i>>1]+(i&1);
	rep(1,maxx,i)
	{
		for(ll j=i;j;j=i&(j-1))
		{
			if(!f[i][j])continue;
			for(ll k=1;k<=n;++k)
			{
				if(!(i&(1<<(k-1))))continue;
				ll w=j;
				if(j&((1<<(k-1))))w=w^(1<<(k-1));
				for(ll cc=1;cc<=n;++cc)
				{
					if((1<<(cc-1))<w)continue;
					if(i&(1<<(cc-1)))continue;
					if(!a[k][cc])continue;
					if(sum[w|(1<<(cc-1))]>K)continue;
					f[i|(1<<(cc-1))][w|(1<<(cc-1))]+=f[i][j];
				}
			}
		}
	}
	ll ans=0;
	for(ll j=maxx;j;j=maxx&(j-1))if(sum[j]==K)ans+=f[maxx][j];
	putl(ans);return 0;
}
posted @ 2020-05-01 15:08  chdy  阅读(248)  评论(0编辑  收藏  举报