/* 题意:
给出一个N个数的序列以及一个k(0<k<=n<=200000),m个操作p,x,y,其中
p=0:将x位置的数替换为y
p=1:将x y位置的数互换
p=2: 查询x-y位置区间连续k个数的和的最大值
解析:求连续区间和最大,可以讲每个区间(我们取左端点)当做一个点,点的附加信息(线段树结子叶点的值)
就等于该区间的连续k个数最大值,可以分为1~k,2~k+1...n-k+1~n,一共从1到n-k+1个点构成线段树。
线段树中除了子叶结点,其他结点都保存的是子叶结点中的最大值。
当x位置值改变时,影响的范围为:
注:我们都是以区间的左端点存入线段树;
(1)假设x位置是某区间的右端点,则其左端点为x-k+1,但是最小端点是从1开始,
x-k+1可能小于1,因此要取这两者最大值;
(2)假设x位置是某区间的左端点,再次说明下,我们是以区间的左端点代表该区间存入线段树,因此当x为左端点
时要与最大左端点n-k+1比较,取最小值;
求出x位置影响范围后,更新区间[max(1,x-k+1),min(x,n-k+1)],找到更新结点后,回溯向上更新即Getmax函数,
当p==1时,区间最值变为_max=_max-v[x]+y;
当p==2时,相当于两次p==1的操作,_max=_max-v[x]+v[y],_max=_max-v[y]+v]x];
每次某位置值改变后,要在原数组中同时更改
最后查询x到y位置区间最值时,我们是以区间左端点代表区间,在线段树中只需查询l=x,r=y-k+1即可
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define inf -0x3f3f3f3f
#define mem0(a) memset(a,0,sizeof(a))
const int maxn = 200000+10;
int sum;
int v[maxn],b[maxn];
struct node{
int l,r,_max,lazy;
}a[maxn<<2];
void Getmax(int cur){
a[cur]._max = max(a[cur<<1]._max,a[cur<<1|1]._max);
}
void build(int l,int r,int cur){
a[cur].l = l;
a[cur].r = r;
a[cur].lazy = 0;
int mid = (l + r )>>1;
if(l == r){
a[cur]._max = b[l];
return ;
}
build(l,mid,cur<<1);
build(mid+1,r,cur<<1|1);
Getmax(cur);
}
void pushdown(int cur){
if(a[cur].lazy){
//设置左右孩子节点的标志域,因为孩子节点可能被多次延迟标记又没有向下传递
//所以是 “+=”
a[cur<<1].lazy += a[cur].lazy;
a[cur<<1|1].lazy += a[cur].lazy;
//根据标志域设置孩子节点的值。因为我们是求区间最大值,因此当区间内每个元
//素加上一个值时,区间的最大值也加上这个值
a[cur<<1]._max += a[cur].lazy;
a[cur<<1|1]._max += a[cur].lazy;
a[cur].lazy = 0;//清除标记
}
}
void update(int l,int r,int v,int cur){
//当前节点区间包含在更新区间内
if( l <= a[cur].l && r >= a[cur].r){
a[cur]._max += v;
a[cur].lazy += v;
return ;
}
pushdown(cur); //延迟标记向下传递
//更新左右孩子节点
int mid = (a[cur].l + a[cur].r )>>1;
if(r <= mid)
update(l,r,v,cur<<1);
else if(l > mid)
update(l,r,v,cur<<1|1);
else {
update(l,mid,v,cur<<1);
update(mid+1,r,v,cur<<1|1);
}
Getmax(cur);//根据左右子树的值回溯更新当前节点的值
}
int query(int l,int r,int cur){
if( l <= a[cur].l && r >= a[cur].r){
return a[cur]._max;
}//若等于要求的区间,则返回结点保存的最值
pushdown(cur);//----延迟标志域向下传递
int mid = (a[cur].l + a[cur].r )>>1;
//分别从左右子树查询,返回两者查询结果的较大值
if(r <= mid )
return query(l,r,cur<<1);
else if(l > mid)
return query(l,r,cur<<1|1);
else {
return max(query(l,mid,cur<<1),query(mid+1,r,cur<<1|1));
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m,k;
scanf("%d%d%d",&n,&m,&k);
for(int i = 1 ;i <= n ; i++)
scanf("%d",&v[i]);
mem0(b);
for(int i = 1 ; i <= k ; i++)
b[1]+=v[i];//第一段和,从1~k
for(int i = 2 ; i <= n-k+1;i++)
b[i]=b[i-1]-v[i-1]+v[i+k-1];
//求剩下每k段和,2~k+1...n-k+1~n
build(1,n-k+1,1);
while(m--){
int c,x,y;
scanf("%d%d%d",&c,&x,&y);
if(c==0){
update(max(1,x-k+1),min(n-k+1,x),y-v[x],1);
v[x]=y;
}
else if(c== 1){
update(max(1,x-k+1),min(n-k+1,x),v[y]-v[x],1);
update(max(1,y-k+1),min(n-k+1,y),v[x]-v[y],1);
swap(v[x],v[y]);
}
else
printf("%d\n",query(x,y-k+1,1));
}
}
return 0;
}
本文介绍了一种使用线段树解决区间更新与查询问题的方法,特别关注于连续k个数的最大和查询。通过构建特殊的线段树,实现区间元素的修改与交换操作,并能高效地查询指定区间内的最大值。
1688

被折叠的 条评论
为什么被折叠?



