题目大意
给出 (n) 个点 (m) 条边的图,给出一个树,问有多少个映射使得树上的边在原图都出现过。
(nle 17,mle n(n-1)/2)
思路
不难看出 (Theta(3^n imes n)) 的 dp,我们可以设 (f_{i,j,S}) 表示以 (i) 为根的子树映射到集合 (S),且 (i) 点映射到 (j) 的方案数。转移式显然。
考虑容斥,我们可以设当前集合为 (S),那么我们在 dp 的时候钦定每个点只能映射到 (S)。这样的意思实际上就是消掉映射重复的方案。
时间复杂度 (Theta(2^n imes n))
( exttt{Code})
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 25
template <typename T> void read (T &x){char c = getchar ();x = 0;int f = 1;while (c < '0' || c > '9') f = (c == '-' ? -1 : 1),c = getchar ();while (c >= '0' && c <= '9') x = x * 10 + c - '0',c = getchar ();x *= f;}
template <typename T,typename ... Args> void read (T &x,Args& ... args){read (x),read (args...);}
template <typename T> void write (T x){if (x < 0) x = -x,putchar ('-');if (x > 9) write (x / 10);putchar (x % 10 + '0');}
bool app[MAXN];
int n,m,f[MAXN][MAXN];
vector <int> G[MAXN],E[MAXN];
void dfs (int u,int fa){
for (Int i = 1;i <= n;++ i) f[u][i] = 1;
for (Int v : G[u]) if (v ^ fa){
dfs (v,u);
for (Int i = 1;i <= n;++ i){
int s = 0;for (Int k : E[i]) s += f[v][k] * (app[i] & app[k]);
f[u][i] *= s;
}
}
}
signed main(){
read (n,m);
for (Int i = 1,u,v;i <= m;++ i) read (u,v),E[u].push_back (v),E[v].push_back (u);
for (Int i = 2,u,v;i <= n;++ i) read (u,v),G[u].push_back (v),G[v].push_back (u);
int ans = 0;for (Int S = 1;S < (1 << n);++ S){
memset (app,0,sizeof (app));int siz = n;
for (Int i = 1;i <= n;++ i) if (S >> i - 1 & 1) app[i] = 1,siz --;
dfs (1,0);for (Int i = 1;i <= n;++ i) ans += f[1][i] * (siz & 1 ? -1 : 1);
}
write (ans),putchar ('
');
return 0;
}