引子
如果把修改扔開,這就是一道十分經典的樹形DP入門題。
然而加上修改,這道題瞬間毒瘤了起來。
先把轉移方程擺在下面:
我們首先考慮處理一種特殊的情況——樹形成了一條鏈。這樣一來,這個轉移式就可以化簡:
這樣一來,我們嘗試用一個矩陣運算來轉移:
其中,定義爲:
那麼,對於一個修改操作,就可以直接更新這個矩陣,用線段樹維護區間,這樣就解決了這個問題。
現在考慮一般的樹。對於一個節點,我們暫時先考慮其中的一個兒子,那麼就能得到:
表示考慮除外的兒子的答案。
在這裏,的地位如此特殊,便不難想到對這棵樹做樹鏈剖分,就是的重兒子。那麼對於重鏈上的信息,模仿上述序列的方法維護;而輕兒子的信息則暴力上傳即可。
接下來正式講講實現。
對於修改操作,我們直接修改目標位置的矩陣。此時,只有該位置的祖先的答案纔有可能變化。於是,修改矩陣以後,更新它所在重鏈的信息,再跳轉至新的重鏈上。此時,原來重鏈的頂端就變爲了該點的輕兒子,故暴力上傳信息即可。之後重複這一過程。
對於查詢操作,由於每一處的最佳決策均轉移至根節點所在重鏈上,所直接查詢根節點所在重鏈即可。總的時間複雜度爲
#include<bits/stdc++.h>
#define lch (i << 1)
#define rch ((i << 1) | 1)
#define mid ((t[i].l + t[i].r) >> 1)
using namespace std;
const int mn = 100005, inf = -(1 << 30);
struct matrix{
int a[2][2];
matrix() {memset(a, 0, sizeof a);}
matrix(int x, int y, int z, int w) {a[0][0] = x, a[0][1] = y, a[1][0] = z, a[1][1] = w;}
matrix operator* (const matrix b) const
{
matrix ret;
for(int i = 0; i < 2; i++)
for(int j = 0; j < 2; j++)
for(int k = 0; k < 2; k++)
ret.a[i][j] = max(ret.a[i][j], a[i][k] + b.a[k][j]);
return ret;
}
}tmp[mn];
struct seg{
int l, r;
matrix val;
}t[mn << 3];
struct edge{
int to, nxt;
}e[mn << 1];
int fir[mn], cnt;
int v[mn], f[mn][2];
int fa[mn], siz[mn], son[mn], top[mn], num[mn], pos[mn], bot[mn], times;
inline void addedge(int a, int b) {e[++cnt] = (edge) {b, fir[a]}, fir[a] = cnt;}
inline int getint()
{
int ret = 0, flg = 1; char c;
while((c = getchar()) < '0' || c > '9')
if(c == '-') flg = -1;
while(c >= '0' && c <= '9')
ret = ret * 10 + c - '0', c = getchar();
return ret * flg;
}
void dfs1(int s, int f)
{
fa[s] = f, siz[s] = 1;
for(int i = fir[s]; i; i = e[i].nxt)
{
int t = e[i].to;
if(t != f)
{
dfs1(t, s), siz[s] += siz[t];
if(siz[son[s]] < siz[t]) son[s] = t;
}
}
}
void dfs2(int s)
{
num[s] = ++times, pos[times] = bot[s] = s;
if(son[s]) top[son[s]] = top[s], dfs2(son[s]), bot[s] = bot[son[s]];
for(int i = fir[s]; i; i = e[i].nxt)
{
int t = e[i].to;
if(t != fa[s] && t != son[s])
top[t] = t, dfs2(t);
}
}
void dfs(int s)
{
f[s][1] = v[s];
for(int i = fir[s]; i; i = e[i].nxt)
{
int t = e[i].to;
if(t != fa[s])
dfs(t), f[s][0] += max(f[t][0], f[t][1]), f[s][1] += f[t][0];
}
}
void make_tree(int i, int l, int r)
{
t[i] = (seg) {l, r, matrix(0, 0, 0, 0)};
if(l == r)
{
int s = pos[l], g0 = 0, g1 = v[s];
for(int i = fir[s]; i; i = e[i].nxt)
{
int t = e[i].to;
if(t != fa[s] && t != son[s])
g0 += max(f[t][0], f[t][1]), g1 += f[t][0];
}
tmp[l] = t[i].val = matrix(g0, g0, g1, inf);
return;
}
make_tree(lch, l, mid), make_tree(rch, mid + 1, r), t[i].val = t[lch].val * t[rch].val;
}
matrix getans(int i, int l, int r)
{
if(t[i].l == l && t[i].r == r) return t[i].val;
if(r <= mid) return getans(lch, l, r);
else if(l > mid) return getans(rch, l, r);
else return getans(lch, l, mid) * getans(rch, mid + 1, r);
}
void edit_tree(int i, int p)
{
if(t[i].l == p && t[i].r == p) {t[i].val = tmp[p]; return;}
if(p <= mid) edit_tree(lch, p);
else edit_tree(rch, p);
t[i].val = t[lch].val * t[rch].val;
}
void edit(int p, int w)
{
tmp[num[p]].a[1][0] += w - v[p], v[p] = w;
while(p)
{
matrix a = getans(1, num[top[p]], num[bot[p]]);
edit_tree(1, num[p]);
matrix b = getans(1, num[top[p]], num[bot[p]]);
p = fa[top[p]];
if(!p) return;
int x = num[p], g0 = a.a[0][0], g1 = a.a[1][0], f0 = b.a[0][0], f1 = b.a[1][0];
tmp[x].a[0][0] = tmp[x].a[0][1] = tmp[x].a[0][0] + max(f0, f1) - max(g0, g1),
tmp[x].a[1][0] += f0 - g0;
}
}
int main()
{
int n = getint(), m = getint(), a, b;
for(int i = 1; i <= n; i++)
v[i] = getint();
for(int i = 1; i < n; i++)
a = getint(), b = getint(), addedge(a, b), addedge(b, a);
dfs1(1, 0), top[1] = 1, dfs2(1), dfs(1), make_tree(1, 1, n);
while(m--)
{
a = getint(), b = getint(), edit(a, b);
matrix ans = getans(1, num[1], num[bot[1]]);
printf("%d\n", max(ans.a[0][0], ans.a[1][0]));
}
}