Address
Solution
令
a
[
i
]
a[i]
a [ i ] 表示点
i
i
i 的权值,
f
[
i
]
f[i]
f [ i ] 表示包含点
i
i
i 的最大连通子块,
g
[
i
]
=
∑
v
∉
s
o
n
[
i
]
f
[
v
]
g[i] = \sum \limits_{v \notin son[i]} f[v]
g [ i ] = v ∈ / s o n [ i ] ∑ f [ v ] ,转移有:
f
[
i
]
=
max
{
0
,
a
[
i
]
+
g
[
i
]
+
f
[
s
o
n
[
i
]
]
}
f[i] = \max\{0, a[i] + g[i] + f[son[i]]\}
f [ i ] = max { 0 , a [ i ] + g [ i ] + f [ s o n [ i ] ] } 如果这题每次只询问
f
[
1
]
f[1]
f [ 1 ] 的话就是裸的 DDP 了。 不过这题好像还是挺裸的 。事实上我们要求的是
max
i
=
1
n
{
f
[
i
]
}
\max\limits_{i = 1}^{n}\{f[i]\}
i = 1 max n { f [ i ] } 。 首先有一个思路:
对每条重链维护
max
{
f
[
i
]
}
\max\{f[i]\}
max { f [ i ] } 。 因为重链的 DFS 序相邻,我们记录每条重链的起点和终点的 DFS 序,并按照终点的 DFS 序对重链排序,那么对于一棵子树包含的所有重链,它们在重链序列上必然是一段区间,可以二分得到。 显然终点在一棵子树内,而起点不在这棵子树内的重链最多只有一条,我们用 RMQ 维护起点 DFS 序最小的重链编号,对这条重链单独查询
m
a
x
{
f
[
i
]
}
max\{f[i]\}
m a x { f [ i ] } ,其余可以用线段树维护区间最大值查询。 修改一个点的权值时,所涉及到的重链只有
O
(
log
n
)
O(\log n)
O ( log n ) 条,每次在线段树上修改
max
{
f
[
i
]
}
\max\{f[i]\}
max { f [ i ] } 即可。
现在我们只剩下一个问题:怎样维护每条重链的
max
{
f
[
i
]
}
\max\{f[i]\}
max { f [ i ] } 。 观察转移方程,我们把
a
[
i
]
+
g
[
i
]
a[i] + g[i]
a [ i ] + g [ i ] 看做点
i
i
i 的权值,
max
{
f
[
i
]
}
\max\{f[i]\}
max { f [ i ] } 即为求重链上的最大子段和,这是线段树的经典问题。 涉及修改一点的祖先的
g
[
i
]
g[i]
g [ i ] ,相当于查询当前这条重链的最大前缀和,同样可以用线段树实现。 这样,我们就做到了单次修改
O
(
log
2
n
)
O(\log^2 n)
O ( log 2 n ) ,单次询问
O
(
log
n
)
O(\log n)
O ( log n ) 解决了这一问题。
Code
由于再写了一个线段树,代码十分地长…… 一个卡常小技巧:线段树维护最大子段和时可能要上传结构体,实际运行较慢;考虑到本题询问的区间是固定的,我们先预处理出这些区间,询问时
f
o
r
for
f o r 一遍合并区间信息即可。
#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 ) ;
}