Solution -「ARC 101E」「AT 4352」Ribbons on Tree
阿新 • • 發佈:2020-08-05
\(\mathcal{Description}\)
Link.
給定一棵 \(n\) 個點的樹,其中 \(2|n\),你需要把這些點兩兩配對,並把每對點間的路徑染色。求使得所有邊被染色的方案數,對 \(10^9+7\) 取模。
\(n\le5000\)。
\(\mathcal{Solution}\)
容斥,令 \(f(S)\) 表示欽定邊集 \(S\) 全部為被覆蓋的方案數。顯然答案為:
\[\sum_{S\subseteq E}(-1)^{|S|}f(S) \]
\(S\) 的斷邊相當於把原樹切分為聯通塊,且塊內可以任意配對。令 \(g(n)\) 表示大小為 \(n\) 的塊任意配對的方案數,則:
\[g(n)=\begin{cases} [n=2]&n\le2\\ (n-1)g(n-2)&\text{otherwise} \end{cases} \]
理解上,考慮在 \(g(n-2)\) 的基礎上新加兩個點。則可以拆開原來的一對點和這兩個點配對,方案數 \(\frac{n-2}2\times2\);這兩個點亦可直接配對,方案數 \(1\)。
利用樹上 DP 求解,令 \(h(u,i)\) 表示 \(u\) 子樹內有 \(i\) 個點與 \(u\) 連通的方案數。樹上揹包轉移:
\[h(u,i)=\sum h(v,j)h(w,i-j) \]
特別地,\(h(u,0)\)
\[h(u,0)=-\sum h(u,i)g(i) \]
答案即為 \(-g(root,0)\)。
\(\mathcal{Code}\)
#include <cstdio> const int MAXN = 5000, MOD = 1e9 + 7; int n, ecnt, head[MAXN + 5], siz[MAXN + 5]; int g[MAXN + 5], f[MAXN + 5][MAXN + 5]; struct Edge { int to, nxt; } graph[MAXN * 2 + 5]; inline void addeq ( int& a, const int b ) { if ( ( a += b ) >= MOD ) a -= MOD; } inline void link ( const int s, const int t ) { graph[++ ecnt] = { t, head[s] }; head[s] = ecnt; } inline void solve ( const int u, const int fa ) { static int tmp[MAXN + 5]; f[u][1] = siz[u] = 1; for ( int i = head[u], v; i; i = graph[i].nxt ) { if ( ( v = graph[i].to ) ^ fa ) { solve ( v, u ); for ( int j = 1; j <= siz[u] + siz[v]; ++ j ) tmp[j] = 0; for ( int j = 0; j <= siz[v]; ++ j ) { for ( int k = 1; k <= siz[u]; ++ k ) { addeq ( tmp[j + k], 1ll * f[v][j] * f[u][k] % MOD ); } } for ( int j = 1; j <= siz[u] + siz[v]; ++ j ) f[u][j] = tmp[j]; siz[u] += siz[v]; } } for ( int i = 2; i <= siz[u]; i += 2 ) addeq ( f[u][0], 1ll * f[u][i] * g[i] % MOD ); f[u][0] = ( MOD - f[u][0] ) % MOD; } int main () { scanf ( "%d", &n ); for ( int i = 1, u, v; i < n; ++ i ) { scanf ( "%d %d", &u, &v ); link ( u, v ), link ( v, u ); } g[0] = 1; for ( int i = 2; i <= n; i += 2 ) g[i] = ( i - 1ll ) * g[i - 2] % MOD; solve ( 1, 0 ); printf ( "%d\n", ( MOD - f[1][0] ) % MOD ); return 0; }