給定一棵 \(n\) 個點的樹,點帶點權。c++
有 \(m\) 次操做,每次操做給定 \(x,y\) ,表示修改點 \(x\) 的權值爲 \(y\) 。git
你須要在每次操做以後求出這棵樹的最大權獨立集的權值大小。ui
如題所示 , 是個模板題 ...spa
首先考慮靜態 \(dp\) , 令 \(dp_{u,0/1}\) 爲 \(u\) 不存在 / 存在 於最大權獨立集的權值大小 .code
而後轉移很顯然 , 一個點存在於獨立集中時 , 兒子全都不能選 . 不存在時 , 兒子可選可不選 .get
令 \(v\) 爲 \(u\) 的兒子 , 那麼轉移就有 :
\[\begin{cases} \displaystyle dp_{u,0} = \sum_{v} \max \{dp_{v,1} dp_{v,0}\} \\ \displaystyle dp_{u,1} = val_u + \sum_v dp_{u,0} \end{cases}\]it
這個若是每次修改直接作是 \(O(n^2)\) 的 , 不能知足要求 .模板
然而此題是隨機數據 . 每次修改單點權值 , 只須要修改這個點到根的 \(dp\) 值就好了 , 指望複雜度 \(O(q \log n + n)\) .class
而後跑的飛快 , 怒拿 Luogu rank2.... 但這種隨便一條鏈 , 或者掃帚就掛了 ..隨機數
下面就要引入正解了 .
也就是對於這種只有加法和取 \(\max\) 操做的 \(dp\) 咱們能夠考慮構造矩陣 .
也就是普通矩陣乘法進行改變 , 把 \(\times\) 變成 \(+\) , \(+\) 變成 \(max\) .
也就是 \(\displaystyle C_{i,j} = \sum_{k} A_{i,k} \times B_{k,j}\) 變成 \(\displaystyle C_{i,j} = \max_{k} \{A_{i,k}+B_{k,j}\}\) 就好了.
不難發現這個仍然知足結合律 . (能夠用展開來證實 , 或者相似於 \(Floyd\) 的方法理解)
首先樹鏈剖分 , 考慮鏈上如何修改和詢問 . 不難構造矩陣 .
\[\begin{bmatrix} 0 & 0\\ val_u & -\infty \end{bmatrix} \times \begin{bmatrix} dp_{i,0} \\ dp_{i,1}\end{bmatrix} = \begin{bmatrix} dp_{i,0} \\ dp_{i,1} \end{bmatrix}\]
而後咱們考慮用線段樹維護前面那個矩陣 , 而後每次 \(O(\log n)\) 修改和詢問就好了 .
但擴展到樹上的時候有些麻煩 , 由於對於一個點可能存在多個兒子 .
但此時它最多隻會有一個重兒子 , 咱們考慮記下他全部的輕兒子對這個點貢獻後的矩陣就好了.
也就是說 令 \(g_{u,0/1}\) 爲以前 \(dp_{u, 0/1}\) 去掉重兒子獲得的答案 , 那麼此時的轉移矩陣就變成了 .
\[\begin{bmatrix} g_{u,0} & g_{u,0}\\ g_{u,1} & 0 \end{bmatrix} \times \begin{bmatrix} dp_{son[u],0} \\ dp_{son[u],1}\end{bmatrix} = \begin{bmatrix} dp_{u,0} \\ dp_{u,1} \end{bmatrix}\]
請本身展開驗證...
而後每次修改的時候 , 只須要在它重鏈底端的矩陣上進行修改就好了 . (第一個點直接改自己)
修改的話 , 就是獲得原來的一個版本和新的一個版本 , 而後對於這個點減去兩個版本的差值就好了 .
注意這個版本是意味着這整條重鏈的版本 , 由於咱們要求的是鏈頂端全部子樹最後貢獻出來的 \(dp\) 值.
詢問的話直接詢問 \(1\) 所在重鏈的答案 . 而後取它選與不選的 \(\max\) 就好了.
注意線段樹中的詢問 , 最好別寫單位矩陣分別乘上左右兩邊的寫法 , 常數會陡增 !!
但仍是介紹一下單位矩陣 ... (本身推得qwq) 對角線全都是 \(0\) , 其餘位置全是 \(-\infty\) .
也就是 \[\displaystyle \begin{bmatrix} 0 & -\infty & \cdots & -\infty \\ -\infty & 0 & \cdots & -\infty \\ \vdots & \cdots & \ddots & \vdots \\-\infty & -\infty & \cdots & -\infty \\ -\infty & -\infty & \cdots & 0\end{bmatrix}\]
最後時間複雜度就是 \(O(n \log^2 n)\) 的... 有點恐怖 , 但跑的仍是算快的 . 矩陣乘法那裏有 \(8\) 的常數有點傷 .
#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) using namespace std; bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0'); return x * fh; } void File() { #ifdef zjp_shadow freopen ("P4643.in", "r", stdin); freopen ("P4643.out", "w", stdout); #endif } const int N = 1e5 + 1e3, Lim = 2, inf = 0x7f7f7f7f; struct Matrix { int a[Lim + 1][Lim + 1]; Matrix () { Set(a, 0); } void Unit() { For (i, 1, Lim) For (j, 1, Lim) a[i][j] = (i == j) ? 0 : -inf; } } ; inline Matrix operator * (Matrix a, Matrix b) { Matrix res; For (i, 1, Lim) For (j, 1, Lim) For (k, 1, Lim) chkmax(res.a[i][j], a.a[i][k] + b.a[k][j]); return res; } void Out(Matrix a) { For (i, 1, Lim) For (j, 1, Lim) printf ("%d%c", a.a[i][j], j == jend ? '\n' : ' '); putchar ('\n'); } vector<int> G[N]; int sz[N], son[N], fa[N]; void Dfs_Init(int u, int from = 0) { sz[u] = 1; fa[u] = from; for (int v : G[u]) if (v ^ from) { Dfs_Init(v, u), sz[u] += sz[v]; if (sz[v] > sz[son[u]]) son[u] = v; } } int dfn[N], id[N], top[N], back[N]; void Dfs_Part(int u) { static int clk = 0; id[dfn[u] = ++ clk] = u; top[u] = son[fa[u]] == u ? top[fa[u]] : u; back[top[u]] = u; if (son[u]) Dfs_Part(son[u]); for (int v : G[u]) if ((v ^ fa[u]) && (v ^ son[u])) Dfs_Part(v); } int dp[N][2], val[N]; void Dp_Pre(int u) { int summax = 0, sum0 = val[u]; for (int v : G[u]) if (v ^ fa[u]) Dp_Pre(v), sum0 += dp[v][0], summax += max(dp[v][0], dp[v][1]); dp[u][0] = summax, dp[u][1] = sum0; } Matrix sub[N]; #define lson o << 1, l, mid #define rson o << 1 | 1, mid + 1, r struct Segment_Tree { Matrix Adv[N << 2]; inline void push_up(int o) { Adv[o] = Adv[o << 1] * Adv[o << 1 | 1]; } void Build(int o, int l, int r) { if (l == r) { int u = id[l], summax = 0, sum0 = val[u]; for (int v : G[u]) if ((v ^ fa[u]) && (v ^ son[u])) summax += max(dp[v][0], dp[v][1]), sum0 += dp[v][0]; Adv[o].a[1][1] = Adv[o].a[1][2] = summax; Adv[o].a[2][1] = sum0; sub[u] = Adv[o]; return ; } int mid = (l + r) >> 1; Build(lson); Build(rson); push_up(o); } void Update(int o, int l, int r, int up) { if (l == r) { Adv[o] = sub[id[l]]; return ; } int mid = (l + r) >> 1; if (up <= mid) Update(lson, up); else Update(rson, up); push_up(o); } Matrix Query(int o, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) return Adv[o]; int mid = (l + r) >> 1; /* Matrix tmp; tmp.Unit(); if (ql <= mid) tmp = tmp * Query(lson, ql, qr); if (qr > mid) tmp = tmp * Query(rson, ql, qr); */ if(qr <= mid) return Query(lson, ql, qr); if(ql > mid) return Query(rson, ql, qr); return Query(lson, ql, qr) * Query(rson, ql, qr); } } T; int n, m; inline void Update(int pos, int uv) { sub[pos].a[2][1] += - val[pos] + uv, val[pos] = uv; Matrix bef, aft; while (pos) { bef = T.Query(1, 1, n, dfn[top[pos]], dfn[back[top[pos]]]); T.Update(1, 1, n, dfn[pos]); aft = T.Query(1, 1, n, dfn[top[pos]], dfn[back[top[pos]]]); pos = fa[top[pos]]; sub[pos].a[1][2] = (sub[pos].a[1][1] += - max(bef.a[1][1], bef.a[2][1]) + max(aft.a[1][1], aft.a[2][1])); sub[pos].a[2][1] += - bef.a[1][1] + aft.a[1][1]; } } inline int Query() { Matrix tmp = T.Query(1, 1, n, dfn[1], dfn[back[top[1]]]); return max(tmp.a[1][1], tmp.a[2][1]); } int main () { File(); n = read(); m = read(); For (i, 1, n) val[i] = read(); For (i, 1, n - 1) { int u = read(), v = read(); G[u].push_back(v); G[v].push_back(u); } Dfs_Init(1); Dfs_Part(1); Dp_Pre(1); T.Build(1, 1, n); while (m --) { int x = read(), y = read(); Update(x, y); printf ("%d\n", Query()); } //cerr << (double) clock() / CLOCKS_PER_SEC << endl; return 0; }