给出一棵n个点、以1为根的有根树,点有点权。要求支持如下两种操作:
M x y:将点x的点权改为y;
Q x:求以x为根的子树的最大连通子块和。
其中,一棵子树的最大连通子块和指的是:该子树所有子连通块的点权和中的最大值
(本题中子连通块包括空连通块,点权和为0)。
由于提高组2018考察了ddp,所以做了一道例题,顺便学习了一下。
这道题首先列出dp方程, f [ x ] = m a x ( 0 , Σ f [ y ] + w [ x ] ) f[x]=max(0,\Sigma f[y]+w[x]) f[x]=max(0,Σf[y]+w[x])(其中y是x的孩子)。这样我们就可以修改 Θ ( 1 ) \Theta(1) Θ(1),但询问 Θ ( x 的 子 树 大 小 ) \Theta(x的子树大小) Θ(x的子树大小),会超时。
考虑链分治,令 g [ x ] = Σ f [ y ] + w [ x ] g[x]=\Sigma f[y]+w[x] g[x]=Σf[y]+w[x](其中y是x的轻孩子),那dp方程就转化成 f [ x ] = m a x ( 0 , f [ y ] + g [ x ] ) f[x]=max(0,f[y]+g[x]) f[x]=max(0,f[y]+g[x])(其中y是x的重孩子),那我们就可以发现这是求最大前缀和的形式。这样我们就可以解决以x为根的子树中包含x的最大连通子块和的查询,修改的话直接修改就可以了。
但问题是题目并不要求强制包含x,那么我们就还要记录一个值s,s[x]=max(f[x],s[y])(y为x的孩子)。从而我们发现询问的答案其实就为x到它所在的那条重链的底端的点的g的最大连续子段和,然后再和每个点的S-top(S-top为s[那个点的轻孩子]的max)取max就是了,那在线段树那里开多一个值ans来记录这个就可以了。那修改的时候,对于每个点要记录它的轻孩子的s值,方便修改,然后由于s[y]每次其实只有最大值有用,那可以对每一个点开一个可删堆。但由于我懒,所以我开了set。
那这道题就解决了,剩下就是一些实现上的细节。
#include
#include
#include
#include
#include
#include
using namespace std;
multiset<long long>s[200010];
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline void write(long long x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
struct node
{
int x,y,next;
}a[400010];int len,last[200010];
inline void ins(int x,int y)
{
len++;
a[len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
int fa[200010],son[200010],tot[200010];
inline void pre_tree_node(int x)
{
tot[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y==fa[x])continue;
fa[y]=x;
pre_tree_node(y);
tot[x]+=tot[y];
if(tot[son[x]]<tot[y])son[x]=y;
}
}
long long g[200010],w[200010],mx[200010];
int id,ys[200010],ex[200010],top[200010],dow[200010];
inline long long pre_tree_edge(int x,int tp)
{
long long f;
f=g[x]=w[x];top[x]=tp;ys[x]=++id;ex[id]=x;
if(son[x]!=0)f+=pre_tree_edge(son[x],tp),dow[x]=dow[son[x]],mx[x]=mx[son[x]];
else dow[x]=x;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y==fa[x] || y==son[x])continue;
long long ul=pre_tree_edge(y,y);
g[x]+=ul;f+=ul;
s[x].insert(mx[y]),mx[x]=max(mx[x],mx[y]);
}
long long wy=max(f,0LL);
mx[x]=max(mx[x],wy);
return wy;
}
struct trnode
{
int l,r,lc,rc;
long long sum,sl,sr,ans;//sumΪgµÄºÍ slΪgµÄ×î´óǰ׺ºÍ srΪgµÄ×î´óºó׺ºÍ
}tr[400010];int trlen;
inline trnode merge(trnode now,trnode lc,trnode rc)
{
now.sum=lc.sum+rc.sum;
now.sl=max(lc.sl,lc.sum+rc.sl);
now.sr=max(rc.sr,rc.sum+lc.sr);
now.ans=max(max(lc.ans,rc.ans),lc.sr+rc.sl);
return now;
}
inline void bt(int l,int r)
{
trlen++;int now=trlen;
tr[now].l=l;tr[now].r=r;
if(l==r)
{
tr[now].sum=g[ex[l]],tr[now].sl=tr[now].sr=max(0LL,g[ex[l]]);
long long wy=0;
if(!s[ex[l]].empty())wy=*s[ex[l]].rbegin();
tr[now].ans=max(g[ex[l]],wy);
}
else
{
int mid=(l+r)>>1;
tr[now].lc=trlen+1;bt(l,mid);
tr[now].rc=trlen+1;bt(mid+1,r);
tr[now]=merge(tr[now],tr[tr[now].lc],tr[tr[now].rc]);
}
}
inline void change(int now,int x)
{
if(tr[now].l==tr[now].r)
{
tr[now].sum=g[ex[x]],tr[now].sl=tr[now].sr=max(0LL,g[ex[x]]);
long long wy=0;
if(!s[ex[x]].empty())wy=*s[ex[x]].rbegin();
tr[now].ans=max(g[ex[x]],wy);
return ;
}
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(x<=mid)change(lc,x);
else change(rc,x);
tr[now]=merge(tr[now],tr[lc],tr[rc]);
}
inline trnode getans(int now,int l,int r)
{
if(tr[now].l==l && tr[now].r==r)return tr[now];
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
if(r<=mid)return getans(lc,l,r);
else if(mid+1<=l)return getans(rc,l,r);
else return merge(tr[0],getans(lc,l,mid),getans(rc,mid+1,r));
}
inline void solve(int x)
{
while(x)
{
int tp=top[x],dw=dow[x],f=fa[tp];
trnode ul=getans(1,ys[tp],ys[dw]);s[f].erase(ul.ans),g[f]-=ul.sl;
change(1,ys[x]);
ul=getans(1,ys[tp],ys[dw]);s[f].insert(ul.ans),g[f]+=ul.sl;
x=f;
}
}
char ss[2];
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int n=read(),m=read();
for(int i=1;i<=n;i++)w[i]=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
ins(x,y),ins(y,x);
}
pre_tree_node(1),pre_tree_edge(1,1);bt(1,n);
while(m--)
{
scanf("%s",ss+1);int x=read();
if(ss[1]=='Q')write(getans(1,ys[x],ys[dow[x]]).ans),puts("");
else
{
int y=read();
g[x]+=y-w[x];solve(x);w[x]=y;
}
}
return 0;
}