【CF53E】Dead Ends 题解
本篇题解是直接状压 dp 题解,内容不含容斥和矩阵树定理。
其他的题解里大多有写到防止算冲,却没有任何一篇题解讲明白如何防止,为什么这样做可以做到不算重。于是我这篇题解就来补充一下证明。
首先我们来设计状态,我们从只有节点
但这样有一个问题,这样有可能会算重,例如样例一中选择
如何才能防止算重呢?我们发现状压 dp 表示的其实是一个在生成树中不断加边的过程,也就是说,如果能做到让生成树能且仅能通过一种加边顺序形成,就能算出答案。
解决方法是:当新加入一个叶子节点是,判断它是否为所有当前叶子节点中序号最大的节点,若是则进行转移,否则不进行转移。
证明:对于一棵生成树,必须先把节点
代码就比较好写了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[1<<10][1<<10],ans;
int n,m,d;
bool g[11][11];
int main()
{
scanf("%d%d%d",&n,&m,&d);
for(int i = 1,u,v;i <= m;i++)
{
scanf("%d%d",&u,&v);
g[u][v] = g[v][u] = 1;
}
for(int i = 2;i <= n;i++)
if(g[1][i]) f[1<<(i-1)|1][1<<(i-1)|1] = 1;
for(int i = 0;i < (1<<n);i++)
{
for(int j = i;j;j = (j-1)&i)
{
if(!f[i][j]) continue;
int c1 = 0,c2 = 0;
for(int k = 1;k <= n;k++)
{
bool p1 = i&(1<<(k-1)),p2 = j&(1<<(k-1));
if(p2) c1++;
else if(p1) c2++;
else continue;
for(int l = 1;l <= n;l++)
{
bool p = i&(1<<(l-1));
if(p||!g[l][k]) continue;
int to;
if(p2) to = j^(1<<(l-1))^(1<<(k-1));
else to = j^(1<<(l-1));
if((to>>(l-1))==1) f[i|(1<<(l-1))][to] += f[i][j];
}
}
if(c1 == d && c2 == n-d) ans += f[i][j];
}
}
printf("%lld\n",ans);
return 0;
}