[5210: 最大连通子块和] 基于变换合并的树上动态 DP 的链分治算法
NOIP居然有动态DP,ORZ学一波。这名字是算法发明者自己取的这么长名字owo
BZOJ5210(非权)
5210: 最大连通子块和
Time Limit: 20 Sec Memory Limit: 128 MB Submit: 292 Solved: 92 [Submit][Status][Discuss]Description
给出一棵n个点、以1为根的有根树,点有点权。要求支持如下两种操作:
M x y:将点x的点权改为y;
Q x:求以x为根的子树的最大连通子块和。
其中,一棵子树的最大连通子块和指的是:该子树所有子连通块的点权和中的最大值
(本题中子连通块包括空连通块,点权和为0)。
Input
第一行两个整数n、m,表示树的点数以及操作的数目。
第二行n个整数,第i个整数w_i表示第i个点的点权。
接下来的n-1行,每行两个整数x、y,表示x和y之间有一条边相连。
接下来的m行,每行输入一个操作,含义如题目所述。保证操作为M x y或Q x之一。
1≤n,m≤200000 ,任意时刻 |w_i|≤10^9 。
Output
对于每个Q操作输出一行一个整数,表示询问子树的最大连通子块和。
Sample Input
5 4
3 -2 0 3 -1
1 2
1 3
4 2
2 5
Q 1
M 4 1
Q 1
Q 2
Sample Output
4
3
1
我们考虑一下在链分治意义下的DP,大概链分支就是对于每个点他只需要维护在其所在重链的信息就可以求出其整棵子树的信息,而对于其轻儿子上的信息就全部加到轻链对应链头在重链上那个点上,这样我们查询就查询重链,而修改就一条链一条链地修,同时一条链修改之后改变其作为轻链而给之上那条重链造成的贡献。
同时我们想一想这道题,我们设定f[x]为联通块包含x这个点,在x这个子树中的最大和。而g[x]我们设定为max( 0 , sigma(f[y]轻) + val[x] ) ,那么最后就有f[x] = f[y重] + g[x]。这样我们发现对于g[x]我们发现单点改变只有在跳链的时候会改变g[x],而对于在一条重链中找出最大的f[x]就是一个求一个最大连续子段和,小白逛公园之(维护从左到右最大,区间最大,从右到左最大,区间和)。
但是这样我们只是找出了一定包含这条重链上某些点的最大联通,那么不包含的其实也很简单,我们对于每个点维护一个multiset维护一下所有从轻儿子上来的最大联通子图的值。这个显然修改的时候也只有跳链的时候修改,之后我们在小白逛公园的单点初始最大值在g[x]和在multiset里面取出一个最大就可以了,这样就做到了询问只查询一个链。
说得很不容易,实际上也很痛苦地写了近三个小时。
时间复杂度:修改(O(nlog^2n),查询O(nlogn)
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<set> #define int long long using namespace std; const int maxn = 400005; int en[maxn],la[maxn],nt[maxn],owo; void adg(int x,int y) { en[++owo]=y; nt[owo]=la[x]; la[x]=owo; } struct mat { int sm,lm,rm,ma; }csz; mat operator*(mat x,mat y) { mat oo; oo.sm = x.sm + y.sm; oo.lm = max(x.lm,x.sm+y.lm); oo.rm = max(y.rm,y.sm+x.rm); oo.ma = max(y.ma,max(x.ma,x.rm+y.lm)); oo.ma = max(oo.ma,max(oo.lm,oo.rm)); return oo; } struct node{ node *ls,*rs; mat oo; }z[maxn],*rt; int tot; int n,m,val[maxn]; int fa[maxn],top[maxn],dfx,dfn[maxn],bot[maxn],siz[maxn],zerz[maxn]; int ps[maxn]; void dfs1(int x,int ba) { siz[x] = 1; fa[x] = ba; for(int it=la[x];it;it=nt[it]) { int y = en[it]; if(y==ba) continue; dfs1(y,x); siz[x] += siz[y]; if(siz[y]>siz[zerz[x]]) zerz[x] = y; } } multiset<int>mx[maxn]; int g[maxn],f[maxn],mm[maxn]; void dfs2(int x,int ace) { dfn[x] = ++dfx; ps[dfx] = x; top[x] = ace; bot[ace] = x; if(zerz[x]) dfs2(zerz[x],ace),mm[x] = mm[zerz[x]]; g[x] = val[x]; mx[x].insert(0); for(int it=la[x];it;it=nt[it]) { int y = en[it]; if(y==fa[x]||y==zerz[x]) continue; dfs2(y,y); mx[x].insert(mm[y]); g[x] += f[y]; } f[x] = max(f[zerz[x]] + g[x] , 0ll ); mm[x] = max(mm[x],f[x]); mm[x] = max(mm[x],*mx[x].rbegin()); } void maketree(node *&p,int l,int r) { p = &z[++tot]; if(l==r) { p->oo.sm = g[ps[l]]; p->oo.lm = max(0ll,g[ps[l]]); p->oo.rm = max(0ll,g[ps[l]]); p->oo.ma = max(0ll,max(g[ps[l]],*mx[ps[l]].rbegin() )); return; } int mid = (l+r)>>1; maketree(p->ls,l,mid); maketree(p->rs,mid+1,r); p->oo = p->ls->oo * p->rs->oo; } void update(node *&p,int l,int r,int x) { if(l==r) { p->oo.sm = g[ps[l]]; p->oo.lm = max(0ll,g[ps[l]]); p->oo.rm = max(0ll,g[ps[l]]); p->oo.ma = max(0ll,max(g[ps[l]],*mx[ps[l]].rbegin() )); return; } int mid = (l+r)>>1; if(x<=mid) update(p->ls,l,mid,x); else update(p->rs,mid+1,r,x); p->oo = p->ls->oo * p->rs->oo; } mat query(node *&p,int l,int r,int x,int y) { if(x<=l&&r<=y) return p->oo; int mid = (l+r)>>1; if(y<=mid) return query(p->ls,l,mid,x,y); else if(x>mid) return query(p->rs,mid+1,r,x,y); else return query(p->ls,l,mid,x,y) * query(p->rs,mid+1,r,x,y); } mat chaxun(int x) { return query(rt,1,n,dfn[x],dfn[bot[top[x]]]); } void change(int x,int y) { mat t1,t2; bool flag = 0; while(x) { if(flag) mx[x].erase(t1.ma),mx[x].insert(t2.ma); t1 = chaxun(top[x]); flag = 1; g[x] += y; update(rt,1,n,dfn[x]); t2 = chaxun(top[x]); y = t2.lm - f[top[x]]; f[top[x]] = t2.lm; x = fa[top[x]]; } } main() { scanf("%lld%lld",&n,&m); for(int i=1;i<=n;i++) { scanf("%lld",&val[i]); } for(int i=1;i<n;i++) { int x,y; scanf("%lld%lld",&x,&y); adg(x,y); adg(y,x); } dfs1(1,0); dfs2(1,1); maketree(rt,1,n); char ss[2]; int x,y; for(int i=1;i<=m;i++) { scanf("%s",&ss[0]); if(ss[0]=='Q') { int x; scanf("%lld",&x); printf("%lld\n",chaxun(x).ma); } else { int x,y; scanf("%lld%lld",&x,&y); change(x,y-val[x]); val[x] = y; } } }