題解 小星星
阿新 • • 發佈:2020-11-20
題目大意
給出 \(n\) 個點 \(m\) 條邊的圖,給出一個樹,問有多少個對映使得樹上的邊在原圖都出現過。
\(n\le 17,m\le n(n-1)/2\)
思路
不難看出 \(\Theta(3^n\times n)\) 的 dp,我們可以設 \(f_{i,j,S}\) 表示以 \(i\) 為根的子樹對映到集合 \(S\),且 \(i\) 點對映到 \(j\) 的方案數。轉移式顯然。
考慮容斥,我們可以設當前集合為 \(S\),那麼我們在 dp 的時候欽定每個點只能對映到 \(S\)。這樣的意思實際上就是消掉對映重複的方案。
時間複雜度 \(\Theta(2^n\times n)\)
\(\texttt{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 ('\n'); return 0; }