传送门
这题一看就是动态dp,先考虑暴力
s表示不选这个点,f表示选这个点
s [ i ] = max ( s [ t ] , f [ t ] ) s[i] = \max(s[t], f[t]) s[i]=max(s[t],f[t])
f [ i ] = max ( 0 , V x + ∑ f [ t ] ) f[i] = \max(0, V_x + \sum f[t]) f[i]=max(0,Vx+∑f[t])
一开始依照套路写个矩乘(雾
虽然上面的转移我硬是推出来了,但是我推了之后发现 s s s并不支持删除一个子树
还是将轻重儿子分开,g表示轻儿子f之和
重链上g的序列代表了什么呢
惊讶的发现 f i f_i fi就是以i为头的最大子段和, s i s_i si就是重链上序列g的最大子段和与轻儿子 s t s_t st 取max
这显然可以方便的使用线段树维护。
修改也变成了单点修改,g可以直接加减维护,单点s可以开一个可删堆维护
#include
#include
#include
using namespace std;
#define int long long
#define dd c=getchar()
int read() {int s=0,w=1;char c;while (dd,c>'9' || c<'0') if (c=='-') w=-1;while (c>='0' && c<='9') s=s*10+c-'0',dd;return s*w;}
#undef dd
void write(int x) {if (x<0) putchar('-'),x=-x;if (x>=10) write(x/10);putchar(x%10|'0');}
void wln(int x) {write(x);putchar('\n');}void wsp(int x) {write(x);putchar(' ');}
const int N = 2e5 + 7;
struct edge {
int t,nxt;
}e[N<<1];
int n,m,cnt;
int V[N],f[N],g[N],s[N];
char c[10];
//=============================================================
int T;
int head[N], siz[N],fa[N],top[N],bot[N],Bs[N],Ms[N],dfn[N],pos[N];
void add(int u, int t) {
e[++cnt].t = t; e[cnt].nxt = head[u]; head[u] = cnt;
}
struct heap {
priority_queue<int> q1,q2;
void push(int x) {
q1.push(x);
}
void erase(int x) {
q2.push(x);
}
int top() {
while (!q1.empty() && !q2.empty() && q1.top() == q2.top()) q1.pop(),q2.pop();
if (q1.empty()) return 0;
return q1.top();
}
}Mx[N];
void dfs(int x, int F) {
siz[x] = 1; fa[x] = F;
for (int i = head[x]; i; i = e[i].nxt) {
int t = e[i].t;
if (t == F) continue;
dfs(t, x);
siz[x] += siz[t];
if (siz[t] > siz[Bs[x]]) Bs[x] = t;
}
}
void dfs1(int x, int tp) {
dfn[++T] = x; pos[x] = T;
top[x] = tp; bot[x] = x;
if (Bs[x]) dfs1(Bs[x], tp), bot[x] = bot[Bs[x]] ,s[x] = s[Bs[x]];
g[x] = V[x];
for (int i = head[x]; i; i = e[i].nxt) {
int t = e[i].t;
if (t == fa[x] || t == Bs[x]) continue;
dfs1(t, t);
g[x] += f[t]; Mx[x].push(s[t]);
}
f[x] = max(0ll, g[x] + f[Bs[x]]); s[x] = max(s[x], max(f[x], Mx[x].top()));
}
//============================================================
struct segtree {
int lc,rc,s,mx;
segtree() {}
segtree operator + (segtree a) {
segtree c;
c.lc = max(lc, s + a.lc);
c.rc = max(a.rc, rc + a.s);
c.s = s + a.s;
c.mx = max(rc + a.lc, max(mx, a.mx));
return c;
}
#define ls (ts<<1)
#define rs (ts<<1|1)
#define lc(x) (tr[x].lc)
#define rc(x) (tr[x].rc)
#define s(x) (tr[x].s)
#define mx(x) (tr[x].mx)
}tr[N<<2];
void build(int ts, int l, int r) {
if (l == r) {
int x = dfn[l];
s(ts) = g[x];
lc(ts) = rc(ts) = max(0ll, g[x]);
mx(ts) = max(g[x], Mx[x].top());
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid); build(rs, mid+1, r);
tr[ts] = tr[ls] + tr[rs];
}
void insert(int ts, int l, int r, int x) {
if (l == r) {
x = dfn[l];
s(ts) = g[x];
lc(ts) = rc(ts) = max(0ll, g[x]);
mx(ts) = max(g[x], Mx[x].top());
return;
}
int mid = (l + r) >> 1;
if (x <= mid) insert(ls, l, mid, x);
else insert(rs, mid+1, r, x);
tr[ts] = tr[ls] + tr[rs];
}
segtree query(int ts, int l, int r, int L, int R) {
if (l == L && r == R) return tr[ts];
int mid = (l + r) >> 1;
if (R <= mid) return query(ls, l, mid, L, R);
else if (L > mid) return query(rs, mid+1, r, L, R);
else return query(ls, l, mid, L, mid) + query(rs, mid+1, r, mid+1, R);
}
void change(int x, int v) {
segtree t1, t2;
bool flg=0;
while (x) {
if (flg) Mx[x].erase(t1.mx), Mx[x].push(t2.mx);
t1 = query(1, 1, n, pos[top[x]], pos[bot[x]]);
flg = 1;
g[x] += v;
insert(1, 1, n, pos[x]);
t2 = query(1, 1, n, pos[top[x]], pos[bot[x]]);
v = t2.lc - f[top[x]]; f[top[x]] = t2.lc;//lc±íʾµÄ¾ÍÊÇf[i]
x = fa[top[x]];
}
}
signed main() {
// freopen("5210.in", "r", stdin);
// freopen("5210.out", "w", stdout);
n = read(); m = read();
for (int i = 1; i <= n; i++) V[i] = read();
for (int i = 1, u, v; i < n; i++) {
u = read(), v = read();
add(u, v); add(v, u);
}
dfs(1,0);
dfs1(1,1);
build(1, 1, n);
for (int i = 1, x, v; i <= m; i++) {
scanf("%s",c);
if (c[0] == 'M') {
x = read(), v = read();
change(x, v - V[x]);
V[x] = v;
}
else {
x = read();
wln(query(1, 1, n, pos[x], pos[bot[x]]).mx);
}
}
}