uoj185ios
設\(f[i][j]\)表示\(i\)爲根的子樹,\(i\)號點對應圖上\(j\)號點時的方案數
顯然這樣\(dp\)會使一些節點使用同一個節點,此時總的節點數就不滿\(n\)個ide
咱們枚舉選的點\(S\),再進行\(dp\)
而後根據選的點數量進行容斥spa
【BZOJ卡不過QAQ】code
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define REP(i,n) for (int i = 1; i <= (n); i++) #define mp(a,b) make_pair<int,int>(a,b) #define cls(s) memset(s,0,sizeof(s)) #define cp pair<int,int> #define LL long long int #define lbt(x) (x & -x) using namespace std; const int maxn = 18,maxm = 100005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();} while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();} return out * flag; } int n,m,fa[maxn],S,G[maxn][maxn],g[maxn][maxn]; LL f[maxn][maxn]; void dfs(int u){ LL sum; for (int i = 1; i <= n; i++) if ((S & (1 << i - 1))) f[u][i] = 1; else f[u][i] = 0; for (int to = 1; to <= n; to++) if (g[u][to] && to != fa[u]){ fa[to] = u; dfs(to); for (int i = 1; i <= n; i++) if (f[u][i]){ sum = 0; for (int j = 1; j <= n; j++) sum += f[to][j] * G[i][j]; f[u][i] *= sum; } } } int main(){ n = read(); m = read(); int a,b; REP(i,m){ a = read(); b = read(); G[a][b] = G[b][a] = 1; } for (int i = 1; i < n; i++){ a = read(); b = read(); g[a][b] = g[b][a] = 1; } int maxv = (1 << n) - 1,cnt; LL sum,ans = 0; for (S = 1; S <= maxv; S++){ cnt = 0; for (int i = S; i; i -= lbt(i)) cnt++; dfs(1); sum = 0; for (int i = 1; i <= n; i++) sum += f[1][i]; if ((n - cnt) & 1) ans -= sum; else ans += sum; } printf("%lld\n",ans); return 0; }