http://uoj.ac/problem/185
首先考虑一个很假的树形 DP. 设 dp[u][p] 表示考虑了以 u 为根的
这个子树, 并且根映射到原图的 p 点. 这个显然可以 O(n3) 转移, 但
是会有不同的点可能映射到同一个点. 于是考虑容斥.
求出 dp(S) 表示映射的点集至多为 S 时的答案, 然后就可以
O(2n*n3) 做了.
#include<cstdio> #define FOR(i,s,t) for(register int i=s;i<=t;++i) typedef long long ll; struct edge{ int to; edge *nxt; }e[666],*las[666],*et=e; int map[666][666]; ll f[666][666]; int a[666]; inline void add(int x,int y){ *++et=(edge){y,las[x]};las[x]=et; } int n,m,x,y,up; ll sum,ans; inline void dp(int now,int fa){ register ll tmp; for(register edge *it=las[now];it;it=it->nxt) if(it->to!=fa) dp(it->to,now); for(register int i=1;i<=a[0];++i){ f[now][i]=1; for(register edge *it=las[now];it;it=it->nxt) if(it->to!=fa){ tmp=0; for(register int j=1;j<=a[0];++j) if(map[a[i]][a[j]]) tmp+=f[it->to][j]; f[now][i]=f[now][i]*tmp; if(!f[now][i]) break; } } } int main(){ scanf("%d%d",&n,&m); FOR(i,1,m){ scanf("%d%d",&x,&y); map[x][y]=map[y][x]=1; } FOR(i,2,n){ scanf("%d%d",&x,&y); add(x,y);add(y,x); } up=(1<<n)-1; for(register int i=1;i<=up;++i){ a[0]=0; FOR(j,0,16) if(i&(1<<j)) a[++a[0]]=j+1; sum=0; dp(1,0); for(register int j=1;j<=a[0];++j) sum=sum+f[1][j]; ans=ans+(((n^a[0])&1)?-sum:sum); } printf("%lld ",ans); return 0; }