P3976 [TJOI2015]旅游
分析
这题和染色,是非常类似的题型。我们从头来看。
本题要求我们动态维护一条链上,从起点到终点中,挑选一个点为买,再挑选一个点为卖,能得到的最大利润(若利润为负则为0,即可以理解为不买不卖)
需要注意的是,因为买的点编号一定要比卖的点靠前(最多刚买的就卖,但这跟不买是一样的),因此我们无法简单的维护一个最大最小值就结束了。
那我们维护什么呢?从最大子段和中得到的提醒,我们维护以下几个值
mx,mi,即一段的最大最小值lmx,表示为从左向右,能得到的利润最大值。(线段树中的从左到右,即为编号从小到大)rmx,表示为从右向左,能得到的利润最大值。(线段树中的从右向左,记为编号从大到小)tag,很常规的区间加懒标记,不多解释了。
其中,关于lmx和rmx的维护,我们来看一下
但请一定记住这点:注意!注意!注意!,是有顺序问题的,因为我们是从起点到终点,是有方向的
lmx的维护,我们分为两部分:- 不跨左右区间,则取左右区间
lmx即可,即为max(left.lmx,right.lmx) - 跨左右区间,则为从左区间到右区间,则取右区间的最大值-左区间的最小值即可,即为
right.mx - left.mi
- 不跨左右区间,则取左右区间
rmx的维护,也分为两部分- 不跨左右区间,则取左右区间
rmx即可,即为max(left.rmx,right.rmx) - 跨左右区间,则为从右区间到左区间,则取左区间的最大值-右区间的最小值即可,即为
left.mx-right.mi
- 不跨左右区间,则取左右区间
我们需要维护从起点翻上来的所有链的信息,我们记为L,还需维护从终点翻上来的所有链的信息,我们记为R
我们首先完成一下初始化
L.lmx = L.rmx = 0;
L.mx = -INF,L.mi = INF;
R = L;
接下来,就要说卡了我超久的一点了。记得我们之前提到的方向问题了嘛?
当我们在更新L,R时,我们需要注意两点:
- 一定要分清楚,更新的是从起点上来的L,还是从终点上来的R。
- 同时在更新L或R时,因为此时用来更新的L或R的链肯定比原来的L,R的编号小,因此都是在L,R左边,要按新区间为左区间,L,R为右区间去更新。
在完成这些之后,最后的答案由两类决定,取其中最大值:
- 不跨区间,则去L的
rmx与R的lmx的最大值,即max(L.rmx,R.lmx) - 跨区间,则取R的最大值-L的最小值,即
R.mx - L.mi
这题,很重要的一点就是,有方向问题,方向问题在树链剖分中也比较常见,需要注意此时操作的是起点的链还是终点的链了
另外在查询的时候,我们需要注意,只有在查询区间横跨左右区间时,我们才进行合并操作,否则直接返回左区间,右区间或整个区间即可
我们来看看代码
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10,M = N*2,INF = 0x3f3f3f3f;
struct Node
{
int l,r,mx,mi,lmx,rmx,tag;
}tr[N<<2];
int h[N],e[M],ne[M],w[N],idx;
int sz[N],dep[N],fa[N],son[N];
int top[N],id[N],nw[N],ts;
int n,m;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa,int depth)
{
sz[u] = 1,fa[u] = pa,dep[u] = depth;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
dfs1(j,u,depth+1);
if(sz[j]>sz[son[u]]) son[u] = j;
sz[u] += sz[j];
}
}
void dfs2(int u,int tp)
{
top[u] = tp,id[u] = ++ts,nw[ts] = w[u];
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa[u]||j==son[u]) continue;
dfs2(j,j);
}
}
void push(Node &u,Node l,Node r)
{
u.lmx = max(max(l.lmx,r.lmx),r.mx - l.mi);
u.rmx = max(max(l.rmx,r.rmx),l.mx - r.mi);
u.mx = max(l.mx,r.mx);
u.mi = min(l.mi,r.mi);
}
void pushup(int u)
{
push(tr[u],tr[u<<1],tr[u<<1|1]);
}
void change(Node &u,int k)
{
u.tag += k;
u.mx += k;
u.mi += k;
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
if(root.tag)
{
change(left,root.tag);
change(right,root.tag);
root.tag = 0;
}
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u] = {l,r,nw[l],nw[l]};
return ;
}
tr[u] = {l,r};
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int l,int r,int k)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
change(tr[u],k);
return ;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
Node query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u];
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l>mid) return query(u<<1|1,l,r);
else if(r<=mid) return query(u<<1,l,r);
else{
Node res;
push(res,query(u<<1,l,r),query(u<<1|1,l,r));
return res;
}
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) scanf("%d",w+i);
for(int i=0;i<n-1;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
dfs1(1,-1,1);
dfs2(1,1);
build(1,1,n);
Node L,R;
scanf("%d",&m);
while(m--)
{
L.lmx = L.rmx = 0;
L.mx = -INF,L.mi = INF;
R = L;
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
while(top[a]!=top[b])
{
if(dep[top[a]]<dep[top[b]])
{
push(R,query(1,id[top[b]],id[b]),R);
modify(1,id[top[b]],id[b],c);
b = fa[top[b]];
}
else
{
push(L,query(1,id[top[a]],id[a]),L);
modify(1,id[top[a]],id[a],c);
a = fa[top[a]];
}
}
if(dep[a]<dep[b])
{
push(R,query(1,id[a],id[b]),R);
modify(1,id[a],id[b],c);
}
else
{
push(L,query(1,id[b],id[a]),L);
modify(1,id[b],id[a],c);
}
printf("%d\n",max(max(L.rmx,R.lmx),R.mx - L.mi));
}
return 0;
}

浙公网安备 33010602011771号