小星星「ZJOI2016」
作者:互联网
题意
给定一个图,以及一颗树。求将树上的节点重新编号后,能够被图覆盖的种类数?节点个数≤17。
思路
考虑朴素的树上dp。
设子状态为\(dp[i][j][s]\),表示节点i的编号为j,包含子树所构成的点集为s的种类数。
转移的时间复杂度为\(O(n3^n)\),完美被卡。
考虑题目给出的限制条件:
一个节点只能被编一次号。
树上每一条边都必须在原图中出现。
如果没有第一个限制,那么我们就可以消掉第三维,转移方程如下:\[dp[i][j]=\Pi_{k\in subtree} \sum_{t=1}^n dp[k][t]\]
在此基础上,我们再考虑第一个限制条件。
当一个节点被重复编号时,肯定会有其他的节点没有被编号,那么我们可以每次枚举哪些点没有被编号。
最后统计答案的时候0个没有-1个没有+2个没有-。。。
总共的时间复杂度是\(O(n2^n)\),刚好跑过。
代码
#include <bits/stdc++.h>
using namespace std;
namespace StandardIO {
template<typename T>inline void read (T &x) {
x=0;T f=1;char c=getchar();
for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T>inline void write (T x) {
if (x<0) putchar('-'),x*=-1;
if (x>=10) write(x/10);
putchar(x%10+'0');
}
}
using namespace StandardIO;
namespace Project {
#define int long long
const int N=20;
int n,m,ans;
int cnt;
int head[N];
struct node {
int from,to,next;
} edge[N<<1];
int G[N][N];
int left[N],dp[N][N];
inline int count (int x) {
int res=0;
while (x) {
if (x&1) ++res;
x>>=1;
}
return res;
}
inline void add (int a,int b) {
edge[++cnt].from=a,edge[cnt].to=b,edge[cnt].next=head[a],head[a]=cnt;
}
void dfs (int now,int fa) {
for (register int i=1; i<=n; ++i) {
if (left[i]) dp[now][i]=0;
else dp[now][i]=1;
}
for (register int i=head[now]; i; i=edge[i].next) {
int to=edge[i].to;
if (to==fa) continue;
dfs(to,now);
for (register int j=1; j<=n; ++j) {
if (left[j]) continue;
int tmp=0;
for (register int k=1; k<=n; ++k) {
if (G[j][k]&&!left[k]) tmp+=dp[to][k];
}
dp[now][j]*=tmp;
}
}
}
inline void MAIN () {
read(n),read(m);
for (register int i=1; i<=m; ++i) {
int x,y;read(x),read(y);
G[x][y]=G[y][x]=1;
}
for (register int i=1; i<n; ++i) {
int x,y;read(x),read(y);
add(x,y),add(y,x);
}
for (register int s=0; s<(1<<n); ++s) {
for (register int i=0; i<n; ++i) {
left[i+1]=s&(1<<i);
}
dfs(1,1);
int tmp=0;
for (register int i=1; i<=n; ++i) {
tmp+=dp[1][i];
}
if (count(s)%2==0) ans+=tmp;
else ans-=tmp;
}
write(ans);
}
#undef int
}
int main () {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
Project::MAIN();
}
标签:cnt,int,小星星,namespace,void,ZJOI2016,节点,dp 来源: https://www.cnblogs.com/ilverene/p/11479578.html