abc222_e Red and Blue Tree(樹上差分+01揹包)
阿新 • • 發佈:2021-12-21
題目大意
給你一棵樹,和一個序列,序列描述了在樹上走的路徑\((a_{i-1} -> a_i)\),你可以給一條邊染成紅色或者藍色,
問走過的紅色邊的條數-藍色邊的條數等於K的方案數。
解題思路
先將式子轉換一下,設sum表示所有邊的經過次數,那麼\(R+B=sum, R-B=K\),可以得到\(R = \frac{sum-K}{2}\),很明顯\(sum-K\)得是偶數並且非負。每條邊的經過次數可以直接暴力算出來,把邊轉換成點,每個點的權值表示這個點的父親與之相連的邊路過的次數,當然也可以樹上差分(寫起來很長,也沒必要,但是順道練練)。然後每條邊相當於一個物品,求01揹包剛好裝成R時的方案數就行了。
程式碼
const int maxn = 1e3+10; const int maxm = 1e5+10; vector<int> e[maxn]; int arr[maxn], sub[maxn], dep[maxn], f[maxn][20]; void dfs(int u) { for (auto v : e[u]) { if (dep[v]) continue; dep[v] = dep[u]+1; f[v][0] = u; for (int i = 1; i<20; ++i) f[v][i] = f[f[v][i-1]][i-1]; dfs(v); } } int lca(int x, int y) { if (dep[x]<dep[y]) swap(x, y); for (int i = 19; i>=0; --i) if (dep[f[x][i]]>=dep[y]) x = f[x][i]; if (x==y) return x; for (int i = 19; i>=0; --i) if (f[x][i]!=f[y][i]) x = f[x][i], y = f[y][i]; return f[x][0]; } vector<int> w; int sum, fx = 1; void dfs2(int u, int p) { for (auto v : e[u]) { if (v==p) continue; dfs2(v, u); sub[u] += sub[v]; } if (sub[u]) w.push_back(sub[u]); else if (u!=1) fx = fx*2%MOD; sum += sub[u]; } int dp[maxm]; int main() { IOS; int n, m, k; cin >> n >> m >> k; for (int i = 1; i<=m; ++i) cin >> arr[i]; for (int i = 1, a, b; i<n; ++i) { cin >> a >> b; e[a].push_back(b); e[b].push_back(a); } dep[1] = 1; dfs(1); for (int i = 2; i<=m; ++i) { ++sub[arr[i-1]]; ++sub[arr[i]]; sub[lca(arr[i-1], arr[i])] -= 2; } dfs2(1, -1); if ((sum+k)%2 || sum+k<0 || sum-k<0) { cout << 0 << endl; return 0; } sum = min(sum+k, sum-k); sum /= 2; dp[0] = 1; for (auto v : w) for (int i = sum; i>=v; --i) dp[i] = (dp[i-v]+dp[i])%MOD; cout << 1LL*dp[sum]*fx%MOD << endl; return 0; }