【树剖】 [TJOI2015]旅游

题目

一棵带点权的树上
每次询问查找 x − > y x->y x>y的路径上先经过 u u u,再经过 v v v a v − a u a_v-a_u avau的最大值
之后将 x − > y x->y x>y路径每个点权加 v v v

题解

额。。。不知道是出题人还是我的语文有问题,我开始认为是一次询问可以多次买入卖出,然后就搞了一个乱七八糟的序列dp,然后炸掉。。。


树剖
思考怎样求答案
假设我们己经将左边,右边的求好了,要合并信息到一起
一共三种情况
① a n s = a n s l ② a n s = a n s r ③ a n s = m a x r − m i n l ①ans=ans_l\\②ans=ans_r\\③ans=max_r-min_l ans=anslans=ansrans=maxrminl
由此可以简单得到我们线段树需要维护的三个值区间最大,最小和 a n s ans ans
但树剖的时候发现 x − > y x->y x>y y − > x y->x y>x是不一样的
所以需要求另一个反过来 a n s ′ = m a x l − m i n r ans'=max_l-min_r ans=maxlminr
以后这种复杂的合并可以考虑用单独函数帮助,代码或许就很简洁好调了吧

#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=2*N,INF=1e9;
int n,m;
int a[N],b[N];
int head[N],nex[M],to[M],tot;
void build(int u,int v){tot++;nex[tot]=head[u];to[tot]=v;head[u]=tot;}
//-
#define ls lson[x]
#define rs rson[x]
int lson[N*4],rson[N*4],tag[N*4];
int t[N*4][2],maxx[N*4],minn[N*4];
void updata(int x)
{
	if(ls&&rs)
	{
		t[x][0]=max(max(t[ls][0],t[rs][0]),maxx[rs]-minn[ls]);
		t[x][1]=max(max(t[ls][1],t[rs][1]),maxx[ls]-minn[rs]);
		minn[x]=min(minn[ls],minn[rs]),maxx[x]=max(maxx[ls],maxx[rs]);
	}
	else if(ls)t[x][0]=t[ls][0],t[x][1]=t[ls][1],minn[x]=minn[ls],maxx[x]=maxx[ls];
	else if(rs)t[x][1]=t[rs][0],t[x][1]=t[rs][1],minn[x]=minn[rs],maxx[x]=maxx[rs];
}
void pushdown(int x)
{
	if(ls)maxx[ls]+=tag[x],minn[ls]+=tag[x],tag[ls]+=tag[x];
	if(rs)maxx[rs]+=tag[x],minn[rs]+=tag[x],tag[rs]+=tag[x];
	tag[x]=0;
}
void build(int x,int l,int r)
{
	if(l==r){maxx[x]=minn[x]=b[l];return ;}
	int mid=(l+r)>>1;
	build(ls=x<<1,l,mid);build(rs=x<<1|1,mid+1,r);
	updata(x);
}
int key;
void add(int x,int l,int r,int ll,int rr)
{
	pushdown(x);
	if(ll<=l&&r<=rr){tag[x]+=key;maxx[x]+=key;minn[x]+=key;return ;}
	int mid=(l+r)>>1;
	if(ll<=mid)add(ls,l,mid,ll,rr);
	if(mid<rr) add(rs,mid+1,r,ll,rr);
	updata(x);
}
void qur(int x,int l,int r,int ll,int rr,int &Mi,int &Ma,int &Tmp0,int &Tmp1)
{
	pushdown(x);
	if(ll<=l&&r<=rr)
	{
		Mi=minn[x],Tmp0=t[x][0];
		Ma=maxx[x],Tmp1=t[x][1];
		return ;
	}
	int mid=(l+r)>>1;
	if(rr<=mid)		qur(ls,l,mid  ,ll,rr,Mi,Ma,Tmp0,Tmp1);
	else if(mid<ll) qur(rs,mid+1,r,ll,rr,Mi,Ma,Tmp0,Tmp1);
	else
	{
		int lmi,rmi,lma,rma,ltmp0,ltmp1,rtmp0,rtmp1;
		qur(ls,l,mid,  ll,rr,lmi,lma,ltmp0,ltmp1);
		qur(rs,mid+1,r,ll,rr,rmi,rma,rtmp0,rtmp1);
		Tmp0=max(max(ltmp0,rtmp0),rma-lmi);
		Tmp1=max(max(ltmp1,rtmp1),lma-rmi);
		Mi=min(lmi,rmi);Ma=max(lma,rma);
	}
	updata(x);
}
//--
int tp[N],son[N],fa[N],dep[N],dfn[N],sz[N],cnt;
void dfs1(int u,int f)
{
	sz[u]=1;dep[u]=dep[f]+1;fa[u]=f;
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];if(v==f)continue;
		dfs1(v,u);sz[u]+=sz[v];
		if(sz[v]>sz[son[u]])son[u]=v;
	}
}
void dfs2(int u,int f)
{
	tp[u]=f;dfn[u]=++cnt;b[cnt]=a[u];
	if(son[u])dfs2(son[u],f);
	for(int i=head[u];i;i=nex[i])
	{
		int v=to[i];if(dfn[v])continue;
		dfs2(v,v);
	}
}
void Q_add(int x,int y)
{
	while(tp[x]^tp[y])
	{
		if(dep[tp[x]]<dep[tp[y]])swap(x,y);
		add(1,1,n,dfn[tp[x]],dfn[x]);
		x=fa[tp[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	add(1,1,n,dfn[y],dfn[x]);
}
int Q_qur(int x,int y)
{
	int ans=0;
	int lma=0,lmi=1e9;
	int rma=0,rmi=1e9;
	int fma=0,fmi=0,ftmp0=0,ftmp1=0;
	while(tp[x]^tp[y])
	{
		if(dep[tp[x]]>dep[tp[y]])
		{
			qur(1,1,n,dfn[tp[x]],dfn[x],fmi,fma,ftmp0,ftmp1);
			ans=max(ans,max(ftmp1,fma-lmi));
			lma=max(lma,fma),lmi=min(lmi,fmi);
			x=fa[tp[x]];
		}
		else
		{
			qur(1,1,n,dfn[tp[y]],dfn[y],fmi,fma,ftmp0,ftmp1);
			ans=max(ans,max(ftmp0,rma-fmi));
			rma=max(rma,fma),rmi=min(rmi,fmi);
			y=fa[tp[y]];
		}
	}
	if(dep[x]>dep[y])
	{
		qur(1,1,n,dfn[y],dfn[x],fmi,fma,ftmp0,ftmp1);
		ans=max(ans,max(ftmp1,fma-lmi));
		lma=max(lma,fma),lmi=min(lmi,fmi);
	}
	else
	{
		qur(1,1,n,dfn[x],dfn[y],fmi,fma,ftmp0,ftmp1);
		ans=max(ans,max(ftmp0,rma-fmi));
		rma=max(rma,fma),rmi=min(rmi,fmi);
	}
	ans=max(ans,rma-lmi);
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int x,y,i=1;i<n;i++)scanf("%d%d",&x,&y),build(x,y),build(y,x);
	memset(minn,0x7f,sizeof(minn));
	dfs1(1,0);dfs2(1,1);build(1,1,n);
	scanf("%d",&m);
	while(m--)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		printf("%d\n",Q_qur(x,y));
		key=z;Q_add(x,y);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值