单点修改,区间查询(仅仅pushup)
I Hate It
样例输入:
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
样例输出:
5
6
5
9
//#include <bits/stdc++.h>
#include <iostream>
#include <stdio.h>
using namespace std;
//#define int long long
const int N=2e5+5;
int n,m;
int w[N];
struct node{//线段树要维护的信息
int l,r;
int v;//该段的最大值
}tr[N*4];
void pushup(int u){
tr[u].v=max(tr[u<<1].v,tr[u<<1|1].v);
}
void build(int u,int l,int r){//二叉树中节点的编号,节点是一段区间
if(l==r){//该段只有一个元素 ,相当于二叉树的叶子节点了
tr[u]={l,r,w[r]};
return;
}
else{
tr[u]={l,r};//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 x,int d){//从节点u对应的区间段往下找到区间上x点,修改成d
if(tr[u].l==x&&tr[u].r==x){//此时节点u对应的区间段长度为1,是x
tr[u].v=d;
}
else{
int mid=(tr[u].l+tr[u].r)>>1;
if(x<=mid){//要找的x在u区间段的左半部分
modify(u<<1,x,d);
}
if(x>=mid+1){
modify(u<<1|1,x,d);
}
pushup(u);//修改完之后,所有的祖先的最大值都要更新
}
}
int query(int u,int l,int r){//从u节点对应的区间段往下分查找[l,r]段
if(tr[u].l>=l&&tr[u].r<=r){
//树中节点完全被包含在[l,r]内了,那么[l,r]的最大值就是tr[u].v
return tr[u].v;
}
int mid=(tr[u].l+tr[u].r)>>1;
int v=0;//woc,谨记以下两条路不一定都走,可能只走一条,必须初始化
if(l<=mid)v=query(u<<1,l,r);//查询区间有部分在左子树中,要搜左子树
if(r>=mid+1)v=max(v,query(u<<1|1,l,r));
//查询区间有部分在右子树中,要搜右子树,[l,r]的最大值就会在 处于左右子树的两个区间中的最大值
return v;
}
signed main(){//单点修改,区间查询
while(scanf("%d%d",&n,&m)!=EOF){
// cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
build(1,1,n);
char op[2];
int x,y;
while(m--){
// cin>>op>>x>>y;
scanf("%s%d%d",op,&x,&y);
if(*op=='Q'){
cout<<query(1,x,y)<<endl;
// printf("%d\n",query(1,x,y));//好奇怪,用printf居然和cout输出的不一样
}
else if(*op=='U'){
modify(1,x,y);
}
}
}
return 0;
}
区间修改,区间查询(pushdown,懒标记下放)
简单整数问题
输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
//#include <bits/stdc++.h>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m;
int w[N];
struct node{//线段树维护的信息
int l,r;
int sum,add;//区间段的和,懒标记
}tr[N*4];
void pushup(int u){//子段往上推
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void pushdown(int u){//由u区间段往下裂开,把u段的信息下放给儿子
node &root=tr[u],&left=tr[u<<1],&right=tr[u<<1|1];
if(root.add){
left.add+=root.add,left.sum+=(left.r-left.l+1)* root.add;//左半边的区间长度
right.add+=root.add,right.sum+=(right.r-right.l+1)* root.add;//右半边的区间长度
root.add=0;//下 放后就清楚标记了
}
}
void build(int u,int l,int r){
if(l==r){//构造一个元素的区间段
tr[u]={l,r,w[r],0};
}
else{
tr[u]={l,r};//勿忘,pushup也只能在递归完u的子段后推出u的sum
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 d){
if(tr[u].l>=l&&tr[u].r<=r){//整棵树都在修改的区间段内
tr[u].sum+=(tr[u].r-tr[u].l+1)*d;
tr[u].add+=d;//标记要累加而不是直接赋值,可能原来u上的标记尚未下放
//届时下放,u这段已经操作过了,当然了,这里全包了就不用下方
}
else{
pushdown(u);//u区间段只有一部分在要求修改区间段内,需将标记下放
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid)modify(u<<1,l,r,d);//要求修改的区间永远都是[l,r],不需要变成[l,mid],可这样有错吗
if(r>=mid+1)modify(u<<1|1,l,r,d);
pushup(u);
}
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){//整棵树都在询问的区间段内
return tr[u].sum;
}
else{
pushdown(u);
int mid=(tr[u].l+tr[u].r)>>1;//不能平分查询区间,而应该是树的区间
// return query(u<<1,l,mid)+query(u<<1|1,mid+1,r);
//注意了下放也是要先判断下的,可能完全在左半边或完全在右半边,
//这样才能使得时间复杂度log2(n)
int sum=0;
if(l<=mid)sum+=query(u<<1,l,r);
if(r>=mid+1)sum+=query(u<<1|1,l,r);
return sum;
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>w[i];
build(1,1,n);
char op;
int x,y,d;
for(int i=1;i<=m;i++){
cin>>op;
if(op=='Q'){
cin>>x>>y;
cout<<query(1,x,y)<<endl;
}
else if(op=='C'){
cin>>x>>y>>d;
modify(1,x,y,d);
}
}
return 0;
}