題目連接php
題意:html
有n個點的一棵樹。其中樹上有m條已知的鏈,每條鏈有一個權值。從中選出任意個不相交的鏈使得鏈的權值和最大。c++
思路:數組
樹形DP。設dp[i]表示i的子樹下的最優權值和,sum[i]表示不考慮i點時子樹的最優權值和,即(j是i的兒子),顯然dp[i]>=sum[i]。那麼問題是考慮i點時dp[i]的值是多少,假設有一條鏈經過i,且端點a和b都在i的子樹裏,即LCA(a,b)=i,若是考慮加上這條鏈的權值,那麼a->i, b->i的路上的點v都不能有鏈通過它們(題目要求鏈不相交),那麼-dp[v],但至少有sum[v],即
,其中v是某條鏈上的點。那麼怎麼快速求出sigma的值呢,想到樹狀數組維護前綴和。那麼怎麼遍歷呢,用DFS序遍歷,思想和「糧食分配」同樣,在L[v]上修改,在R[v]上恢復。this
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; const int D = 20; struct Chain { int u, v, w; }; vector<Chain> chains[N]; vector<int> edges[N]; int dp[N], sum[N]; int n, m; int tim; void init() { for (int i=1; i<=n; ++i) { edges[i].clear (); chains[i].clear (); } } int rt[N][D], dep[N]; void init_LCA() { for (int j=1; j<D; ++j) { for (int i=1; i<=n; ++i) { rt[i][j] = rt[i][j-1] ? rt[rt[i][j-1]][j-1] : 0; } } } int LCA(int u, int v) { if (dep[u] < dep[v]) swap (u, v); for (int i=0; i<D; ++i) { if ((dep[u] - dep[v]) >> i & 1) { u = rt[u][i]; } } if (u == v) return u; for (int i=D-1; i>=0; --i) { if (rt[u][i] != rt[v][i]) { u = rt[u][i]; v = rt[v][i]; } } return rt[u][0]; } struct BIT { int C[N]; int n; void init(int n) { this->n = n; memset (C, 0, sizeof (C)); } void updata(int i, int x) { for (; i<=n; i+=i&-i) C[i] += x; } int query(int i) { int ret = 0; for (; i>0; i-=i&-i) ret += C[i]; return ret; } }bsum, bdp; int L[N], R[N]; void DFS(int u, int pa) { L[u] = tim++; dep[u] = dep[pa] + 1; rt[u][0] = pa; for (auto v: edges[u]) { if (v == pa) continue; DFS (v, u); } R[u] = tim; } void DFS(int u) { sum[u] = 0; for (auto v: edges[u]) { if (v == rt[u][0]) continue; DFS (v); sum[u] += dp[v]; } dp[u] = sum[u]; for (auto chain: chains[u]) { int a = chain.u, b = chain.v, c = chain.w; int tmp = bsum.query (L[a]) - bdp.query (L[a]) + bsum.query (L[b]) - bdp.query (L[b]); dp[u] = max (dp[u], sum[u] + tmp + c); } bsum.updata (L[u], sum[u]); bsum.updata (R[u], -sum[u]); bdp.updata (L[u], dp[u]); bdp.updata (R[u], -dp[u]); } void prepare() { dep[0] = 0; tim = 1; DFS (1, 0); init_LCA (); bsum.init (n); bdp.init (n); } int main() { int T; scanf ("%d", &T); while (T--) { init (); scanf ("%d%d", &n, &m); for (int i=1; i<n; ++i) { int u, v; scanf ("%d%d", &u, &v); edges[u].push_back (v); edges[v].push_back (u); } prepare (); for (int i=1; i<=m; ++i) { int u, v, w; scanf ("%d%d%d", &u, &v, &w); int lca = LCA (u, v); chains[lca].push_back ((Chain) {u, v, w}); } DFS (1); printf ("%d\n", dp[1]); } return 0; }