bzoj5210最大连通子块和
-
题解:
- 考虑朴素的dp:$$f_{u} = max(\sum_{v} f_{v} + w_{u} , 0) \ \ \ \ h_{u} = max( max_{v} \{ h_{v} \} , h_{u} )$$
- 考虑利用树剖修改:记$son_{u}$为$u$的重儿子,$g_{u}$为$u$所有轻儿子之和加$w_{u}$;
- 方程变成:
- $g_{u} = w_{u} + \sum_{v!=son_{u}} f_{v} $
- $f_{u} = max(f_{son_{u}}+ g_{u} , 0) $
- $h_{u} = max( max_{v} \{h_{v} \} , h_{u} )$
- 一个一个讨论:
- 首先$g$直接修改:当修改一个点时,$w_{u}$改变$g_{u}$改变,向上跳到轻链继续修改,这只有$log$次;
- 注意到$h$在一条重链上被写成了$g$的最大子段和,同时一个点的$f$就是$g$的最大左段和,这可以用线段树维护;
- 还需要维护用来统计答案的$h$,对每个点用一个堆记录轻儿子的$h$值,再把堆顶元素一起存到线段树里即可;
- 查询只需要查询一个点到链底的区间;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define ll long long #define ls (k<<1) #define rs (k<<1|1) using namespace std; const int N=200010; int n,m,w[N],sz[N],st[N],ed[N],fa[N],tp[N],sn[N],hd[N],o,idx,id[N]; ll f[N],g[N],h[N],s[N<<2],sl[N<<2],sr[N<<2],ss[N<<2],mx[N<<2]; struct Edge{int v,nt;}E[N<<1]; struct data{ priority_queue<ll>A,B; void push(ll x){A.push(x);} void pop(ll x){B.push(x);} ll top(){ while(!B.empty()&&A.top()==B.top())A.pop(),B.pop(); return A.empty()?0:A.top(); } }q[N]; char gc(){ static char*p1,*p2,s[1000000]; if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin); return(p1==p2)?EOF:*p1++; } int rd(){ int x=0,f=1;char c=gc(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=gc();} return x*f; } int gt(){ char c=gc();while(!isalpha(c))c=gc(); return c=='M'; } void adde(int u,int v){ E[o]=(Edge){v,hd[u]};hd[u]=o++; E[o]=(Edge){u,hd[v]};hd[v]=o++; } void dfs1(int u,int F){ sz[u]=1;sn[u]=0; f[u]=w[u]; for(int i=hd[u];~i;i=E[i].nt){ int v=E[i].v; if(v==F)continue; dfs1(v,u); fa[v]=u; sz[u]+=sz[v]; f[u]+=f[v]; if(sz[v]>sz[sn[u]])sn[u]=v; if(h[u]<h[v])h[u]=h[v]; } if(f[u]<0)f[u]=0; if(h[u]<f[u])h[u]=f[u]; } void dfs2(int u,int T){ tp[u]=T; id[st[u]=++idx]=u; if(sn[u])dfs2(sn[u],T),ed[u]=ed[sn[u]]; else ed[u]=idx; g[u]=w[u]; for(int i=hd[u];~i;i=E[i].nt){ int v=E[i].v; if(v==fa[u]||v==sn[u])continue; dfs2(v,v); g[u]+=f[v]; q[u].push(h[v]); } } void init(int k,int l){ ss[k]=g[id[l]]; mx[k]=q[id[l]].top(); s[k]=sl[k]=sr[k]=max(g[id[l]],0ll); } void pushup(int k){ mx[k]=max(mx[ls],mx[rs]); s[k]=max(max(s[ls],s[rs]),sr[ls]+sl[rs]); sl[k]=max(sl[ls],ss[ls]+sl[rs]); sr[k]=max(sr[ls]+ss[rs],sr[rs]); ss[k]=ss[ls]+ss[rs]; } void build(int k,int l,int r){ if(l==r){init(k,l);return;} int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); pushup(k); } void update(int k,int l,int r,int x){ if(l==r){init(k,l);return;} int mid=(l+r)>>1; if(x<=mid)update(ls,l,mid,x); else update(rs,mid+1,r,x); pushup(k); } ll S,Sl,Sr,Mx,Ss; void query(int k,int l,int r,int x,int y){ if(l==x&&r==y){ Mx=max(Mx,mx[k]); S=max(max(S,s[k]),Sr+sl[k]); Sl=max(Sl,Ss+sl[k]); Sr=max(Sr+ss[k],sr[k]); Ss=Ss+ss[k]; return ; } int mid=(l+r)>>1; if(y<=mid)query(ls,l,mid,x,y); else if(x>mid)query(rs,mid+1,r,x,y); else query(ls,l,mid,x,mid),query(rs,mid+1,r,mid+1,y); } void Update(int x,int y){ int tx=tp[x]; g[x]-=w[x];g[x]+=(w[x]=y); while(tx!=1){ update(1,1,n,st[x]); S=Sl=Sr=Ss=Mx=0; query(1,1,n,st[tx],ed[tx]); if(Sl!=f[tx]){ g[fa[tx]]-=f[tx]; g[fa[tx]]+=(f[tx]=Sl); } Mx=max(Mx,S); if(h[tx]!=Mx){ q[fa[tx]].pop(h[tx]); q[fa[tx]].push(h[tx]=Mx); } x=fa[tx],tx=tp[x]; } update(1,1,n,st[x]); } void Query(int x){ S=Sl=Sr=Ss=Mx=0; query(1,1,n,st[x],ed[x]); Mx=max(Mx,S); printf("%lld\n",Mx); } int main(){ // freopen("bzoj5210.in","r",stdin); // freopen("bzoj5210.out","w",stdout); n=rd();m=rd(); for(int i=1;i<=n;++i)hd[i]=-1,w[i]=rd(); for(int i=1;i<n;++i)adde(rd(),rd()); dfs1(1,0); dfs2(1,1); build(1,1,n); for(int i=1,x,y;i<=m;++i){ if(gt())x=rd(),y=rd(),Update(x,y); else x=rd(),Query(x); } return 0; }