Address
Solution
- 令
a[i] 表示点
i 的权值,
f[i] 表示包含点
i 的最大连通子块,
g[i]=v∈/son[i]∑f[v],转移有:
f[i]=max{0,a[i]+g[i]+f[son[i]]}
- 如果这题每次只询问
f[1] 的话就是裸的 DDP 了。
不过这题好像还是挺裸的 。
- 事实上我们要求的是
i=1maxn{f[i]}。
- 首先有一个思路:
- 对每条重链维护
max{f[i]}。
- 因为重链的 DFS 序相邻,我们记录每条重链的起点和终点的 DFS 序,并按照终点的 DFS 序对重链排序,那么对于一棵子树包含的所有重链,它们在重链序列上必然是一段区间,可以二分得到。
- 显然终点在一棵子树内,而起点不在这棵子树内的重链最多只有一条,我们用 RMQ 维护起点 DFS 序最小的重链编号,对这条重链单独查询
max{f[i]},其余可以用线段树维护区间最大值查询。
- 修改一个点的权值时,所涉及到的重链只有
O(logn) 条,每次在线段树上修改
max{f[i]} 即可。
- 现在我们只剩下一个问题:怎样维护每条重链的
max{f[i]}。
- 观察转移方程,我们把
a[i]+g[i] 看做点
i 的权值,
max{f[i]} 即为求重链上的最大子段和,这是线段树的经典问题。
- 涉及修改一点的祖先的
g[i],相当于查询当前这条重链的最大前缀和,同样可以用线段树实现。
- 这样,我们就做到了单次修改
O(log2n),单次询问
O(logn) 解决了这一问题。
Code
- 由于再写了一个线段树,代码十分地长……
- 一个卡常小技巧:线段树维护最大子段和时可能要上传结构体,实际运行较慢;考虑到本题询问的区间是固定的,我们先预处理出这些区间,询问时
for 一遍合并区间信息即可。
#include <iostream>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
template <class T>
inline void read(T &res)
{
char ch; bool flag = false; res = 0;
while (ch = getchar(), !isdigit(ch) && ch != '-');
ch == '-' ? flag = true : res = ch ^ 48;
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
flag ? res = -res : 0;
}
const int S = 1 << 20;
char fwt[S], *ohead = fwt;
const char *otail = ohead + S;
inline void outChar(char ch)
{
if (ohead == otail)
fwrite(fwt, 1, S, stdout), ohead = fwt;
*ohead++ = ch;
}
template <class T>
inline void put(T x)
{
if (x > 9) put(x / 10);
outChar(x % 10 + 48);
}
typedef long long ll;
const int L = 8e5 + 5;
const int N = 2e5 + 5;
const int M = 4e5 + 5;
int idt[N], fa[N], top[N], dep[N], pos[N], idx[N], sze[N], son[N];
int n, m, T, qm, id;
int va[N], ed[N], Log[N], g[18][N], _seg[N][18];
ll val[N], f[N], mx[L];
template <class T>
inline T Max(T x, T y) {return x > y ? x : y;}
template <class T>
inline T Min(T x, T y) {return x < y ? x : y;}
template <class T>
inline void CkMax(T &x, T y) {x < y ? x = y : 0;}
struct seg
{
int l, r;
seg() {}
seg(int L, int R):
l(L), r(R) {}
inline bool operator < (const seg &a) const
{
return r < a.r;
}
}q[N];
inline int idMin(int x, int y) {return q[x].l < q[y].l ? x : y;}
struct Tree
{
ll sum, pre, suf, ans;
Tree() {}
Tree(ll Sum, ll Pre, ll Suf, ll Ans):
sum(Sum), pre(Pre), suf(Suf), ans(Ans) {}
friend inline Tree operator + (const Tree &a, const Tree &b)
{
Tree c;
c.sum = a.sum + b.sum;
c.pre = Max(a.sum + b.pre, a.pre);
c.suf = Max(a.suf + b.sum, b.suf);
c.ans = Max(Max(a.ans, b.ans), a.suf + b.pre);
return c;
}
}tr[L];
struct Edge
{
int to; Edge *nxt;
}p[M], *lst[N], *P = p;
inline void Link(int x, int y)
{
(++P)->nxt = lst[x]; lst[x] = P; P->to = y;
(++P)->nxt = lst[y]; lst[y] = P; P->to = x;
}
inline void Dfs1(int x)
{
dep[x] = dep[fa[x]] + 1;
sze[x] = 1;
f[x] = va[x];
for (Edge *e = lst[x]; e; e = e->nxt)
{
int y = e->to;
if (y == fa[x]) continue;
fa[y] = x;
Dfs1(y);
sze[x] += sze[y];
sze[y] > sze[son[x]] ? son[x] = y : 0;
f[x] += Max(f[y], 0ll);
}
}
inline void Dfs2(int x)
{
if (son[x])
{
pos[son[x]] = ++T;
idx[T] = son[x];
top[son[x]] = top[x];
Dfs2(son[x]);
}
int y;
for (Edge *e = lst[x]; e; e = e->nxt)
if (y = e->to, !top[y])
{
pos[y] = ++T;
idx[T] = y;
top[y] = y;
Dfs2(y);
}
}
inline void Init()
{
Dfs1(1);
pos[1] = idx[1] = top[1] = T = 1;
Dfs2(1);
for (int x = 1; x <= n; ++x)
if (top[x] == x)
{
int y;
for (y = x; son[y]; y = son[y]);
ed[x] = y;
q[++qm] = seg(pos[x], pos[y]);
}
}
#define sL s << 1
#define sR s << 1 | 1
inline void Uptdate(int s)
{
tr[s] = tr[sL] + tr[sR];
}
inline void Build(int s, int l, int r)
{
if (l == r)
{
int x = idx[l], y; ll g = va[x];
for (Edge *e = lst[x]; e; e = e->nxt)
if (y = e->to, y != son[x] && y != fa[x])
g += Max(f[y], 0ll);
val[l] = g;
tr[s] = Tree(g, g, g, g);
return ;
}
int mid = l + r >> 1;
Build(sL, l, mid);
Build(sR, mid + 1, r);
Uptdate(s);
}
inline void findSeg(int s, int l, int r, int x, int y)
{
if (l == x && r == y)
return (void)(_seg[id][++_seg[id][0]] = s);
int mid = l + r >> 1;
if (y <= mid)
findSeg(sL, l, mid, x, y);
else if (x > mid)
findSeg(sR, mid + 1, r, x, y);
else
{
findSeg(sL, l, mid, x, mid);
findSeg(sR, mid + 1, r, mid + 1, y);
}
}
inline void Modify(int s, int l, int r, int x)
{
if (l == r)
{
ll g = val[x];
tr[s] = Tree(g, g, g, g);
return ;
}
int mid = l + r >> 1;
x <= mid ?
Modify(sL, l, mid, x) : Modify(sR, mid + 1, r, x);
Uptdate(s);
}
inline Tree queryTree_quick(int t)
{
Tree res = tr[_seg[t][1]];
for (int i = 2, im = _seg[t][0]; i <= im; ++i)
res = res + tr[_seg[t][i]];
return res;
}
inline Tree queryTree(int s, int l, int r, int x, int y)
{
if (l == x && r == y)
return tr[s];
int mid = l + r >> 1;
if (y <= mid)
return queryTree(sL, l, mid, x, y);
else if (x > mid)
return queryTree(sR, mid + 1, r, x, y);
else
return queryTree(sL, l, mid, x, mid)
+ queryTree(sR, mid + 1, r, mid + 1, y);
}
inline void Uptdate2(int s)
{
mx[s] = Max(mx[sL], mx[sR]);
}
inline void Build2(int s, int l, int r)
{
if (l == r)
return (void)(mx[s] = queryTree(1, 1, n, q[l].l, q[l].r).ans);
int mid = l + r >> 1;
Build2(sL, l, mid); Build2(sR, mid + 1, r);
Uptdate2(s);
}
inline void Modify2(int s, int l, int r, int x, ll v)
{
if (l == r)
return (void)(mx[s] = v);
int mid = l + r >> 1;
x <= mid ?
Modify2(sL, l, mid, x, v) : Modify2(sR, mid + 1, r, x, v);
Uptdate2(s);
}
inline ll queryMax(int s, int l, int r, int x, int y)
{
if (l == x && r == y)
return mx[s];
int mid = l + r >> 1;
if (y <= mid)
return queryMax(sL, l, mid, x, y);
else if (x > mid)
return queryMax(sR, mid + 1, r, x, y);
else
return Max(queryMax(sL, l, mid, x, mid),
queryMax(sR, mid + 1, r, mid + 1, y));
}
inline void pathModify(int x, int v)
{
val[pos[x]] += v - va[x];
va[x] = v;
while (top[x] > 1)
{
int w = idt[top[x]];
ll lst = queryTree_quick(w).pre;
Modify(1, 1, n, pos[x]);
Tree tmp = queryTree_quick(w);
Modify2(1, 1, qm, w, tmp.ans);
ll now = tmp.pre;
x = fa[top[x]];
val[pos[x]] += Max(now, 0ll) - Max(lst, 0ll);
}
int w = idt[top[x]];
Modify(1, 1, n, pos[x]);
Modify2(1, 1, qm, w, queryTree_quick(w).ans);
}
inline int queryId(int x, int y)
{
int k = Log[y - x + 1];
return idMin(g[k][x], g[k][y - (1 << k) + 1]);
}
int main()
{
read(n); read(m);
for (int i = 1; i <= n; ++i)
read(va[i]);
for (int i = 1, x, y; i < n; ++i)
{
read(x); read(y);
Link(x, y);
}
Init();
Build(1, 1, n);
std::sort(q + 1, q + qm + 1);
for (int i = 1; i <= qm; ++i)
id = i, findSeg(1, 1, n, q[i].l, q[i].r);
Log[0] = -1;
for (int i = 1; i <= qm; ++i)
g[0][i] = i, Log[i] = Log[i >> 1] + 1;
for (int j = 1, jm = Log[qm]; j <= jm; ++j)
for (int i = 1; i + (1 << j) - 1 <= qm; ++i)
g[j][i] = idMin(g[j - 1][i], g[j - 1][i + (1 << j - 1)]);
for (int i = 1; i <= qm; ++i)
idt[idx[q[i].l]] = i;
Build2(1, 1, qm);
char opt; int x, v;
while (m--)
{
while (opt = getchar(), opt != 'Q' && opt != 'M');
if (opt == 'Q')
{
read(x);
int tl = std::lower_bound(q + 1, q + qm + 1, seg(0, pos[x])) - q,
tr = std::upper_bound(q + 1, q + qm + 1, seg(0, pos[x] + sze[x] - 1)) - q - 1,
w = queryId(tl, tr);
ll ans = 0;
if (q[w].l < pos[x])
{
CkMax(ans, queryTree(1, 1, n, pos[x], q[w].r).ans);
if (w > tl) CkMax(ans, queryMax(1, 1, qm, tl, w - 1));
if (w < tr) CkMax(ans, queryMax(1, 1, qm, w + 1, tr));
}
else
CkMax(ans, queryMax(1, 1, qm, tl, tr));
put(ans), outChar('\n');
}
else
{
read(x); read(v);
pathModify(x, v);
}
}
fwrite(fwt, 1, ohead - fwt, stdout);
}