【BZOJ5210】最大連通子塊和 樹剖線段樹+動態DP


【BZOJ5210】最大連通子塊和

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

題解首先思路源自WC2018上陳俊錕所說的動態DP。

$O(n^2)$做法:

我們考慮每次詢問時都進行一次樹形DP。我們令f[x]表示x子樹中,包含x的連通塊的權值和最大值(可以為空),容易得到DP方程:$f[x]=max(0,v[x]+\sum f[y])$(y是x的兒子)。再維護s[x]表示x的子樹中連通塊的權值和最大值,$s[x]=max(f[x],s[y]) $。如果每次詢問都進行一次樹形DP的話,修改的復雜度是O(1),查詢的復雜度是O(x的子樹大小)。但是我們發現每次修改時只會對x到根這一條鏈上的點的DP值造成影響,其中對f[]的影響可以直接加減得到,對s[]的影響我們可以通過對每個點都開一個可刪除堆維護。這樣一來查詢的復雜就變成了O(1),但是修改的復雜度變成了O(x的深度*log n),依然無法通過此題。

$O(n\log^2n)$做法:

我們考慮優化上面說的第二種暴力,不難想到用樹剖+線段樹來維護。我們令$g[x]=\sum f[y] $(y是x的輕兒子)。那么重鏈上的轉移就變成了$f[x]=max(0,f[son[x]]+g[x])$。你會驚訝的發現這個式子就是最大連續子段和的形式!所以在修改時,我們從x沿着不斷往根跳,對於重鏈,在線段樹上維護g[x]的最大連續子段和;對於輕鏈,暴力維護DP值即可。

接下來考慮如何維護s[]。我們沿用上面的思路,對每個點維護一個可刪除堆,但是這里的堆維護的是x的所有輕兒子的s值,重兒子的s值我們放到線段樹上同最大連續子段和一起維護(代碼中記為sm,表示max(g值的最大連續子段和,所有點的輕兒子的s))。那么我們在線段樹上更新時,只需要用堆頂的元素來更新最大連續子段和,便完成了s[x]從底往上的傳遞。

在查詢時,我們只需要將 x所在鏈的鏈底—x 在線段樹上對應的區間 的最大連續子段和取出來即可。

這樣一次修改的復雜度是$O(log^2n)$的,一次詢問的復雜度是$O(\log n)$的。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=200010;
typedef long long ll;
int n,m,cnt;
int to[maxn<<1],next[maxn<<1],head[maxn],p[maxn],q[maxn],siz[maxn],dep[maxn],fa[maxn],top[maxn],son[maxn],sson[maxn];
ll v[maxn],f[maxn],g[maxn],ms[maxn];
char str[5];
struct heap
{
	priority_queue<ll> p1,p2;
	inline void push(ll x) {p1.push(x);}
	inline void erase(ll x) {p2.push(x);}
	inline ll top()
	{
		while(!p2.empty()&&p1.top()==p2.top())	p1.pop(),p2.pop();
		if(p1.empty())	return 0;
		return p1.top();
	}
}mx[maxn];
struct sag
{
	ll sl,sr,sm,s;
	sag () {}
	sag operator + (const sag &a) const
	{
		sag b;
		b.s=s+a.s;
		b.sl=max(sl,s+a.sl);
		b.sr=max(a.sr,sr+a.s);
		b.sm=max(sr+a.sl,max(sm,a.sm));
		return b;
	}
}s[maxn<<2];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
	return ret*f;
}
void dfs1(int x)
{
	siz[x]=1;
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
	{
		fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs1(to[i]),siz[x]+=siz[to[i]];
		if(siz[to[i]]>siz[son[x]])	son[x]=to[i];
	}
}
void dfs2(int x,int tp)
{
	top[x]=tp,p[x]=++q[0],q[q[0]]=x;
	if(son[x])	dfs2(son[x],tp),sson[x]=sson[son[x]],ms[x]=ms[son[x]];
	else	sson[x]=x;
	g[x]=v[x];
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x]&&to[i]!=son[x])
		dfs2(to[i],to[i]),g[x]+=f[to[i]],mx[x].push(ms[to[i]]);
	f[x]=max(0ll,g[x]+f[son[x]]),ms[x]=max(ms[x],f[x]),ms[x]=max(ms[x],mx[x].top());
}
void build(int l,int r,int x)
{
	if(l==r)
	{
		s[x].s=g[q[l]];
		s[x].sl=s[x].sr=max(g[q[l]],0ll);
		s[x].sm=max(g[q[l]],mx[q[l]].top());
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson),build(mid+1,r,rson);
	s[x]=s[lson]+s[rson];
}
void updata(int l,int r,int x,int a)
{
	if(l==r)
	{
		s[x].s=g[q[l]];
		s[x].sl=s[x].sr=max(g[q[l]],0ll);
		s[x].sm=max(g[q[l]],mx[q[l]].top());
		return ;
	}
	int mid=(l+r)>>1;
	if(a<=mid)	updata(l,mid,lson,a);
	else	updata(mid+1,r,rson,a);
	s[x]=s[lson]+s[rson];
}
sag query(int l,int r,int x,int a,int b)
{
	if(a<=l&&r<=b)	return s[x];
	int mid=(l+r)>>1;
	if(b<=mid)	return query(l,mid,lson,a,b);
	if(a>mid)	return query(mid+1,r,rson,a,b);
	return query(l,mid,lson,a,b)+query(mid+1,r,rson,a,b);
}
inline void modify(int x,ll y)
{
	sag t1,t2,t3;
	int flag=0;
	while(x)
	{
		t3=query(1,n,1,p[top[x]],p[sson[top[x]]]);
		if(flag)	mx[x].erase(t1.sm),mx[x].push(t2.sm);
		t1=t3;
		flag=1;
		g[x]+=y,updata(1,n,1,p[x]);
		t2=query(1,n,1,p[top[x]],p[sson[top[x]]]);
		y=t2.sl-f[top[x]],f[top[x]]=t2.sl;
		x=fa[top[x]];
	}
}
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	//freopen("A.in","r",stdin);
	//freopen("A.out","w",stdout);
	n=rd(),m=rd();
	int i,a,b;
	memset(head,-1,sizeof(head));
	for(i=1;i<=n;i++)	v[i]=rd();
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	dep[1]=1,dfs1(1),dfs2(1,1);
	build(1,n,1);
	for(i=1;i<=m;i++)
	{
		scanf("%s",str);
		if(str[0]=='M')
		{
			a=rd(),b=rd();
			modify(a,b-v[a]),v[a]=b;
		}
		else
		{
			a=rd();
			printf("%lld\n",query(1,n,1,p[a],p[sson[a]]).sm);
		}
	}
	return 0;
}//5 3 1 2 -3 -4 5 1 2 2 3 3 4 4 5 Q 2 C 4 1 Q 2


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM