P7706 「Wdsr-2.7」文文的摄影布置 题解

题目大意

有两个长度为 nnn 的序列 aaabbb,给定 mmm 次操作,每次输入 333 个整数 op,x,yop,x,yop,x,y,对于 op=1op = 1op=1 的操作我们把 axa_xax 修改为 yyy,对于 op=2op = 2op=2 的操作我们把 bxb_xbx 修改为 yyy,对于 op=3op = 3op=3 的操作我们输出 max⁡x≤l≤l+1<r≤yψ(l,r)\max\limits_{x \le l \le l+1 < r \le y} \psi(l,r)xll+1<rymaxψ(l,r),其中 ψ(i,k)=ai+ak−min⁡i<j<kbj\psi(i,k) = a_i + a_k - \min \limits _{i<j<k} b_jψ(i,k)=ai+aki<j<kminbj

解决

不难看出这道题是一道线段树合并的题。线段树功能:单点修改,区间查询。

这颗线段树每个节点需要维护的信息有:

  • aaa,表示该节点及它子树中 aaa 的最大值。

  • bbb,表示该节点及它子树中 bbb 的最小值。

  • lanslanslans,表示对于该节点 iiiai−bja_i - b_jaibj 的最大值,其中 j>ij>ij>i

  • ransransrans,表示对于该节点 iiiak−bja_k - b_jakbj 的最大值,其中 j<kj<kj<k

  • ansansans,即答案 ψ\psiψ

给出代码:

struct Tree
{
	int a,b,lans,rans,ans;
	Tree()
	{
		lans=rans=ans=-0x3f3f3f3f;
	}
};
Tree tree[2000010];

现在开始建树。因为 aaabbb 不是一起输入的,所以先输入所有的 aaa 再 build。

给出主函数代码:

for(int i=1;i<=n;i++) read(tmp[i]);

给出 build 代码:

inline void build(int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt].a=tmp[l],read(tree[rt].b);
		return;
	}
	int mid=(l+r)/2;
	build(ls),build(rs),pushup(rt);
}

给出 pushup 代码,因为本题只是单点修改,所以不涉及懒惰标记和 pushdown:

inline void pushup(const int &rt)
{
	tree[rt].a=max(tree[rt*2].a,tree[rt*2+1].a),tree[rt].b=min(tree[rt*2].b,tree[rt*2+1].b);
	tree[rt].lans=max(tree[rt*2].lans,max(tree[rt*2+1].lans,tree[rt*2].a-tree[rt*2+1].b));
	tree[rt].rans=max(tree[rt*2].rans,max(tree[rt*2+1].rans,tree[rt*2+1].a-tree[rt*2].b));
	tree[rt].ans=max(max(tree[rt*2].ans,tree[rt*2+1].ans),max(tree[rt*2].lans+tree[rt*2+1].a,tree[rt*2+1].rans+tree[rt*2].a));
}

这里对于第一种和第二种修改没有什么可说的,直接看查询。

查询区间为 L,RL,RL,R,查询节点区间为 l,rl,rl,r,如果查询区间完全包含了当前区间,则直接返回当前节点。

查询区间与当前节点的交集只在 l,midl,midl,mid 之间,则直接返回继续查询左儿子即可。

查询区间与当前节点的交集只在 mid+1,rmid + 1,rmid+1,r 之间,则直接返回继续查询右儿子即可。

如果在左儿子和右儿子都有,则把查询左右儿子的返回值合并再返回即可。

代码:

inline Tree ask(const int &L,const int &R,int l,int r,int rt)
{
	if(l>=L && r<=R) return tree[rt];
	int mid=(l+r)/2;
	if(mid>=R) return ask(L,R,ls);
	if(mid<L) return ask(L,R,rs);
	Tree ret,x=ask(L,R,ls),y=ask(L,R,rs);
	ret.a=max(x.a,y.a),ret.b=min(x.b,y.b),ret.ans=max(max(x.ans,y.ans),max(x.lans+y.a,y.rans+x.a));
	ret.lans=max(x.lans,max(y.lans,x.a-y.b)),ret.rans=max(x.rans,max(y.rans,y.a-x.b));
	return ret;
}

下面给出完整代码:

#include<bits/stdc++.h>
using namespace std;
#define ls l,mid,rt*2
#define rs mid+1,r,rt*2+1
#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
struct Tree
{
	int a,b,lans,rans,ans;
	Tree()
	{
		lans=rans=ans=-0x3f3f3f3f;
	}
};
Tree tree[2000010];
inline void read(int &x,char c=Getchar())
{
	bool f=c!='-';
	x=0;
	while(c<48 or c>57) c=Getchar(),f&=c!='-';
	while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
	x=f?x:-x;
}
inline void write(int x)
{
	if(x<0) Putchar('-'),x=-x;
	if(x>=10) write(x/10),x%=10;
	Putchar(x^48);
}
int n,m,tmp[500010];
inline void pushup(const int &rt)
{
	tree[rt].a=max(tree[rt*2].a,tree[rt*2+1].a),tree[rt].b=min(tree[rt*2].b,tree[rt*2+1].b);
	tree[rt].lans=max(tree[rt*2].lans,max(tree[rt*2+1].lans,tree[rt*2].a-tree[rt*2+1].b));
	tree[rt].rans=max(tree[rt*2].rans,max(tree[rt*2+1].rans,tree[rt*2+1].a-tree[rt*2].b));
	tree[rt].ans=max(max(tree[rt*2].ans,tree[rt*2+1].ans),max(tree[rt*2].lans+tree[rt*2+1].a,tree[rt*2+1].rans+tree[rt*2].a));
}
inline void build(int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt].a=tmp[l],read(tree[rt].b);
		return;
	}
	int mid=(l+r)/2;
	build(ls),build(rs),pushup(rt);
}
inline void fix1(const int &pos,const int &val,int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt].a=val;
		return;
	}
	int mid=(l+r)/2;
	if(mid>=pos) fix1(pos,val,ls);
	else fix1(pos,val,rs);
	pushup(rt);
}
inline void fix2(const int &pos,const int &val,int l,int r,int rt)
{
	if(l==r)
	{
		tree[rt].b=val;
		return;
	}
	int mid=(l+r)/2;
	if(mid>=pos) fix2(pos,val,ls);
	else fix2(pos,val,rs);
	pushup(rt);
}
inline Tree ask(const int &L,const int &R,int l,int r,int rt)
{
	if(l>=L && r<=R) return tree[rt];
	int mid=(l+r)/2;
	if(mid>=R) return ask(L,R,ls);
	if(mid<L) return ask(L,R,rs);
	Tree ret,x=ask(L,R,ls),y=ask(L,R,rs);
	ret.a=max(x.a,y.a),ret.b=min(x.b,y.b),ret.ans=max(max(x.ans,y.ans),max(x.lans+y.a,y.rans+x.a));
	ret.lans=max(x.lans,max(y.lans,x.a-y.b)),ret.rans=max(x.rans,max(y.rans,y.a-x.b));
	return ret;
}
int main()
{
	read(n),read(m);
	for(int i=1;i<=n;i++) read(tmp[i]);
	build(1,n,1);
	for(int i=1,op,x,y;i<=m;i++)
	{
		read(op),read(x),read(y);
		if(op==1) fix1(x,y,1,n,1);
		else if(op==2) fix2(x,y,1,n,1);
		else write(ask(x,y,1,n,1).ans),Putchar('\n');
	}
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值