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\n",ans);
return 0;
}