线段树板子

15 篇文章 0 订阅
5 篇文章 0 订阅

单点修改,区间查询(仅仅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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值