将树镶嵌在图里面,等价于用树去覆盖这个图,每个点都被覆盖一次。
每个点都被覆盖一次,等价于,每个点至少被覆盖一次
方案数=所有点都可以覆盖-至少有1个点未被覆盖+至少有2个点未被覆盖-至少有3个点未被覆盖……(这里的图上的点可以被树上的点重复覆盖)
求某些点不能被覆盖的方案数,可以用树上dp完成,
Fi,j
表示树上第
i
个点对应图上第
爆枚图上有哪些点一定不能被覆盖,按容斥系数加到答案里面去就行了
树上dp用
O(N2)
的状态,单次转移是
O(deg(u)+N)
,树的度数和是
N
,爆枚状态的时间复杂度是
总时间复杂度
O(2NN3)
,空间复杂度
O(N2)
#include <bits/stdc++.h>
#define N 20
#define M 500
using namespace std;
typedef long long LL;
vector<int> E[N];
struct Edge{int b,n;}e[M];
int h[N],vis[N],cnt,n,m;
LL F[N][N];
inline int rd() {int r;scanf("%d",&r);return r;}
void link(int a,int b) {
e[++cnt] = (Edge){b,h[a]}, h[a] = cnt;
e[++cnt] = (Edge){a,h[b]}, h[b] = cnt;
}
void dfs(int u,int f) {
for (int i=0;i<(int)E[u].size();i++)
if (E[u][i] != f) dfs(E[u][i], u);
for (int _=1;_<=n;_++) if (vis[_]) {
F[u][_] = 1;
for (int i=0;i<(int)E[u].size();i++) {
int v = E[u][i]; if (v == f) continue;
LL cur = 0LL;
for (int j=h[_],x;x=e[j].b,j;j=e[j].n) cur += F[v][x];
F[u][_] = 1LL * F[u][_] * cur;
}
}
}
LL g(int x) {
LL ret = 0LL;
for (int _=1;_<=n;_++,x>>=1) vis[_] = (x&1)^1;
dfs(1,1);
for (int _=1;_<=n;_++) ret += F[1][_];
return ret;
}
int main() {
n = rd(), m = rd();
for (int _=1;_<=m;_++) link(rd(), rd());
for (int _=1;_<n;_++) {
int a = rd(), b = rd();
E[a].push_back(b);
E[b].push_back(a);
}
int tp = (1<<n) - 1;
LL ans = 0LL;
for (int _=0;_<=tp;_++) {
memset(F,0,sizeof(F));
int x = __builtin_popcount(_);
x&1 ? ans -= g(_) : ans += g(_);
}
cout << ans << endl;
return 0;
}