线段树

线段树是一种二叉搜索树,用于存储区间信息。本文介绍了线段树的基本概念、建树过程,以及单点查询、单点修改、区间查询和区间修改的操作。通过实例解释了线段树在区间查询和修改上的高效性,并提供了多道相关题目以加深理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线段树

线段树是一棵二叉搜索树,它储存的是一个区间的信息。下面是一个简单的线段树。
在这里插入图片描述
由上图可得,
1、每个节点的左孩子区间范围为[l,mid],右孩子为[mid+1,r] (mid=(l+r)/2)
2、对于结点k,左孩子结点为2k,右孩子为2k+1,这符合完全二叉树的性质
废话少说,下面是线段树的基本操作:

struct node
{
       int l,r,w;//l,r分别表示区间左右端点,w表示区间和
}tree[400000];

先用结构体构建一个树,其实也不是一定要建一个结构体,你也可以构建几个单独的数组来建树;

建树

void build(int l,int r,int k)
{
    tree[k].l=l;tree[k].r=r;
    if(l==r)//叶子节点     {
        scanf("%d",&tree[k].w);
        return ; 
    }
    int m=(l+r)/2;
    build(l,m,k*2);//左孩子 
    build(m+1,r,k*2+1);//右孩子 
    tree[k].w=tree[k*2].w+tree[k*2+1].w;//状态合并,此结点的w=两个孩子的w之和 

k就是上面辣个图的红色的数字,l和r就是代表一个区间的左右段,也就是上面图中蓝色的数字。关于这个mid的求法有些童鞋可能有点迷惑,比如mid=(1+3)/2=2,然后左边1-2,右边3-3,那构建出来的树不就右边相比于左边是缺了一点的吗?没关系,有几个k没被处理到也没关系,就把它空在那里是没有影响的,只要保证符合左孩子是 2k 右孩子是 2k+1 就可以了。
可能还有些童鞋对这个建树不大理解,那你可以举几个例子,自己按着这个函数的思路走一遍就会理解的。

单点查询

void ask(int k)
{
    if(tree[k].l==tree[k].r) //当前结点的左右端点相等,是叶子节点,是最终答案     {
        ans=tree[k].w;
        return ;
    }
    int m=(tree[k].l+tree[k].r)/2;
    if(x<=m) ask(k*2);//目标位置比中点靠左,就递归左孩子 
    else ask(k*2+1);//反之,递归右孩子 
}

这就是单点查询的操作,我们要查找编号为m的树是什么,然后从ask(1)太太太太太爷爷开始往下找到这个为m-m的孙子(比如找3,一共有10个,那找的顺序为1-10 —>1-5—>1-3—>3-3—>ans取3的值),当然也可以定义为int ask(int k) ,我这就不多写了。

单点修改

void add(int k){    
if(tree[k].l==tree[k].r)//找到目标位置     
{       tree[k].w+=y;
       return; 
}
int m=(tree[k].l+tree[k].r)/2;
if(x<=m) add(k*2);    
else add(k*2+1);    
tree[k].w=tree[k*2].w+tree[k*2+1].w;//所有包含结点k的结点状态更新
}

和单点查询思路差不多,w指的是区间所有数的和。一般线段树都有类似与w这样储存区间信息的数,这里只不过储存的是区间和。

区间查询

void sum(int k){    
if(tree[k].l>=x&&tree[k].r<=y)     {      
	ans+=tree[k].w;        
	return ;    
	}    
int m=(tree[k].l+tree[k].r)/2;   
if(x<=m) sum(k*2);   
if(y>m) sum(k*2+1);
 }

ans储存的就是所求区间[x,y]的和,当然这个函数也可以设为int类型。如果对这种sum函数里面又有一个sum这样套来套去不理解的,最好的方法就是举个栗子从sum(1)开始套一遍直到最后结果出来,然后你就会理解了。

区间修改

区间修改是改一个区间,但是如果你一开始就一个一个去改的话,可能会浪费很多时间,所以我们用一个标记(数字)来标记这个区间,代表区间里面的值都要进行这个标记所代表的操作,这个标记也叫做 懒标记。(一般需要区间修改时才会用到懒标记)
代码需要两个函数,一个为:
懒标记下穿代码:f为懒标记,其余变量与前面含义一致。

void down(int k) {    
tree[k*2].f+=tree[k].f;    
tree[k*2+1].f+=tree[k].f;    
tree[k*2].w+=tree[k].f*(tree[k*2].r-tree[k*2].l+1);
tree[k*2+1].w+=tree[k].f*(tree[k*2+1].r-tree[k*2+1].l+1);
tree[k].f=0;
}

另一个为:

Ⅲ 完整的区间修改代码:
void add(int k)
{   
if(tree[k].l>=a&&tree[k].r<=b)//当前区间全部对要修改的区间有用     
{        
	tree[k].w+=(tree[k].r-tree[k].l+1)*x;//(r-1)+1区间点的总数
	tree[k].f+=x;
	return;    
	}
if(tree[k].f) down(k);//懒标记下传。只有不满足上面的if条件才执行,所以一定会用到当前节点的子节点 
int m=(tree[k].l+tree[k].r)/2;
if(a<=m) add(k*2);
if(b>m) add(k*2+1);
tree[k].w=tree[k*2].w+tree[k*2+1].w;//更改区间状态 
}

这里是对区间[a,b]里的每个数加x,大家注意看,是区间的l>=a且区间的r<=b,是指当前区间在范围[a,b]里面在对这个区间加上x*(这个区间的数的个数),然后对这个区间加一个懒标记x,如果日后其他操作会涉及到这个区间的里面去,那就会进行这个down吧懒标记移到他的左右儿子那里。

接下来是几个题目:
hdu1754: I Hate It

很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。

Input

本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取’Q’或’U’) ,和两个正整数A,B。
当C为’Q’的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为’U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。

Output

对于每一次询问操作,在一行里面输出最高成绩。

Sample Input

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

Sample Output

5
6
5
9

简单的线段树题目,用到了单点查询和区间查询,不像前面的w那样存区间和,这题我们存区间最大值。

#include<iostream>
#include<algorithm> 
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
struct node
{
	int l,r,maxx;
}tree[900005];
int n,m,a,b;
void build(int k,int l,int r)//建树
{
	tree[k].l=l;
	tree[k].r=r;
	if(l==r) {
		cin>>tree[k].maxx;
		return ;
	}
	int mid=(l+r)/2;
	build(2*k,l,mid);
	build(2*k+1,mid+1,r);
	tree[k].maxx=max(tree[2*k].maxx,tree[2*k+1].maxx);//存区间最大和
}
void change(int k)//单点修改
{
	if(tree[k].l==tree[k].r) {
		if(tree[k].l==a)
		tree[k].maxx=b;
		return ;	
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(a<=mid) change(2*k);
	else change(2*k+1);
	tree[k].maxx=max(tree[2*k].maxx,tree[2*k+1].maxx);
}
int find(int k)//区间查询
{	int ans1=0,ans2=0;
	if(tree[k].l>=a&&tree[k].r<=b) {
		return tree[k].maxx;
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(a<=mid) ans1=max(ans1,find(2*k));
	if(b>mid) ans2=max(ans2,find(2*k+1));
	return max(ans1,ans2);
}
int main()
{	IOS;
	char s;
	while(cin>>n>>m)
	{
		build(1,1,n);
		for(int i=1;i<=m;i++) {
			cin>>s>>a>>b;
			if(s=='Q') {
				int sum=find(1);
				cout<<sum<<endl;
			}
			else {
				change(1);
			}
		}
	}
return 0;
}

Codevs 1082:
题目描述 Description
给你N个数,有两种操作:

1:给区间[a,b]的所有数增加X

2:询问区间[a,b]的数的和。

输入描述 Input Description

第一行一个正整数n,接下来n行n个整数,

再接下来一个正整数Q,每行表示操作的个数,

如果第一个数是1,后接3个正整数,

表示在区间[a,b]内每个数增加X,如果是2,

表示操作2询问区间[a,b]的和是多少。

输出描述 Output Description

对于每个询问输出一行一个答案

样例输入 Sample Input
3
1
2
3
2
1 2 3 2
2 2 3

样例输出 Sample Output
9

数据范围
1<=n<=200000
1<=q<=200000

考查区间修改和区间查询,这里就要用到懒标记了。

#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 200005
typedef long long LL;
struct node{
	int l,r,f;
	LL w;
}tree[maxn<<2];
int n,q,op,a,b,c;
void build(int k,int l,int r){
	tree[k].l=l;
	tree[k].r=r;
	if(l==r){
		scanf("%d",&tree[k].w);
		return;
	}
	int mid=(l+r)/2;
	build(k<<1,l,mid);
	build((k<<1)+1,mid+1,r);
	tree[k].w=tree[k<<1].w+tree[(k<<1)+1].w; 
}
void down(int k){//懒标记下传
	tree[k<<1].f+=tree[k].f;
	tree[(k<<1)+1].f+=tree[k].f;
	tree[k<<1].w+=tree[k].f*(tree[k<<1].r-tree[k<<1].l+1);
	tree[(k<<1)+1].w+=tree[k].f*(tree[(k<<1)+1].r-tree[(k<<1)+1].l+1);
	tree[k].f=0;
}
void change_interval(int k){//区间修改
	if(tree[k].l>=a&&tree[k].r<=b){
		tree[k].w+=c*(tree[k].r-tree[k].l+1);
		tree[k].f+=c;
		return;
	}
	if(tree[k].f)
	down(k);
	int mid=(tree[k].l+tree[k].r)/2;
	if(a<=mid)
	change_interval(k<<1);
	if(b>mid)
	change_interval((k<<1)+1);
	tree[k].w=tree[k<<1].w+tree[(k<<1)+1].w;
}
LL ask_interval(int k){//区间查询
	LL ans=0;
	if(tree[k].l>=a&&tree[k].r<=b){
		ans+=tree[k].w;
		return ans;
	}
	if(tree[k].f)
	down(k);
	int mid=(tree[k].l+tree[k].r)/2;
	if(a<=mid)
	ans+=ask_interval(k<<1);
	if(b>mid)
	ans+=ask_interval((k<<1)+1);
	return ans;
}
int main()
{
	scanf("%d",&n);
	memset(tree,0,sizeof(tree));
	build(1,1,n);
	scanf("%d",&q);
	while(q--){
		scanf("%d",&op);
		if(op==1){
			scanf("%d%d%d",&a,&b,&c);
			change_interval(1);
		}
		else{
			scanf("%d%d",&a,&b);
			LL ans=ask_interval(1);
			printf("%lld\n",ans);	
		}
	}
	return 0;
}

poj2777 Count Color :

有一块很长的板,长度为L厘米,L是一个正整数,所以我们可以把板均匀地分成L段,它们用1,2。。。L从左到右,每根都有1厘米长。现在我们要给电路板上色-一段只有一种颜色。我们可以在板上做以下两个操作:

  1. "C A B C“用C色将板从A段涂到B段。

  2. "P A B“输出A段和B段(包括)之间绘制的不同颜色的数量。

在我们的日常生活中,我们用很少的词来描述一种颜色(红、绿、蓝、黄……),所以你可能会认为不同颜色的总数T很小。为了简单起见,我们将颜色的名称表示为颜色1,颜色2。。。一开始,木板被涂成了颜色1。现在剩下的问题就交给你了。

Input
First line of input contains L (1 <= L <= 100000), T (1 <= T <= 30) and O (1 <= O <= 100000). Here O denotes the number of operations. Following O lines, each contains “C A B C” or “P A B” (here A, B, C are integers, and A may be larger than B) as an operation defined previously.
Output
Ouput results of the output operation in order, each line contains a number.

Sample Input
2 2 4
C 1 1 2
P 1 2
C 2 2 2
P 1 2

Sample Output
2
1
这题我们需要用到二进制的知识,首先颜色种类并不多,我们可以用二进制存有哪些颜色在这个区间比如000000010代表只有2号颜色,如果对位运算符|和>>和<<不懂的,可以去百度查看。

#include<iostream>
using namespace std;
int n,m,o,a,b,c,ans;
struct mlp
{
	int l,r,w;
	bool f;
}tree[300000];
void build(int k,int l,int r)
{
	tree[k].l=l;
	tree[k].r=r;
	tree[k].w=1;
	tree[k].f=1;
	if(l==r) {
		return ;
	}
	int mid=(l+r)/2;
	build(2*k,l,mid);
	build(2*k+1,mid+1,r);
}
void down(int k)
{
	tree[2*k].w=tree[k].w;
	tree[2*k+1].w=tree[k].w;
	tree[2*k].f=tree[2*k+1].f=1;
	tree[k].f=0;
}
void change(int k)
{
	if(tree[k].l>=a&&tree[k].r<=b) {
		tree[k].w=(1<<(c-1));
		tree[k].f=1;
		return ;
	}
	if(tree[k].w==(1<<(c-1))) return ;
	if(tree[k].f) down(k);
	int mid=(tree[k].l+tree[k].r)/2;
	if(a<=mid) change(2*k);
	if(b>mid) change(2*k+1);
	tree[k].w=tree[2*k].w|tree[2*k+1].w; 
}
void find(int k)
{
	if(tree[k].l>=a&&tree[k].r<=b) {
		ans=ans|tree[k].w;
		return ;
	}
	if(ans==tree[k].w) return ;
	if(tree[k].f) down(k);
	int mid=(tree[k].l+tree[k].r)/2;
	if(a<=mid) find(2*k);
	if(b>mid) find(2*k+1);
	tree[k].w=tree[2*k].w|tree[2*k+1].w; 
}
//int find(int k)
//{	int ans,ans1,ans2;
//	ans=ans1=ans2=0;
//	if(tree[k].l>=a&&tree[k].r<=b) {
//		ans=tree[k].w;
//		return ans;
//	}	
//	if(tree[k].f) down(k);
//	int mid=(tree[k].l+tree[k].r)/2;
//	if(a<=mid) ans1=find(2*k);
//	if(b>mid) ans2=find(2*k+1);
//	ans=ans1|ans2;
//	return ans;
//}
int solve(int k)
{
	int ans=0;
	while(k) {
		if(k%2==1) {
			ans++;
		}
		k/=2;
	}
	return ans;
}
int main()
{	cin>>n>>m>>o;
	char s;
	build(1,1,n);
	for(int i=1;i<=o;i++) {
		cin>>s;
		if(s=='C') {
			cin>>a>>b>>c;
			if(a>b) swap(a,b);
			change(1);
		}
		else {
			cin>>a>>b;
			if(a>b) swap(a,b);
			ans=0;
			find(1);
			cout<<solve(ans)<<endl;
		}
	}
	
return 0;
}

如果上面的题目你都懂了,那么现在告诉你,线段树还有一种题型,就是需要用到区间合并,请看下题:

**SPOJ - GSS1 Can you answer these queries I **

You are given a sequence A[1], A[2], …, A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A query is defined as follows:
Query(x,y) = Max { a[i]+a[i+1]+…+a[j] ; x ≤ i ≤ j ≤ y }.
Given M queries, your program must output the results of these queries.

Input
The first line of the input file contains the integer N.
In the second line, N numbers follow.
The third line contains the integer M.
M lines follow, where line i contains 2 numbers xi and yi.

Output
Your program should output the results of the M queries, one query per line.

Input:
3
-1 2 3
1
1 2

Output:
2

#include<iostream>
#include<algorithm>
using namespace std;
int n,m,x,y;
struct mmp
{
	int sum,gss,lgss,rgss;
	int l,r;
}tree[400001];
void build(int k,int l,int r)
{	tree[k].l=l;
	tree[k].r=r;
	if(l==r) {
		cin>>tree[k].sum;
		tree[k].lgss=tree[k].gss=tree[k].rgss=tree[k].sum;
		return ;
	}
	int mid=(l+r)/2;
	build(2*k,l,mid);
	build(2*k+1,mid+1,r);
	tree[k].sum=tree[2*k].sum+tree[2*k+1].sum;
	tree[k].lgss=max(tree[2*k].lgss,tree[2*k].sum+tree[2*k+1].lgss);
	tree[k].rgss=max(tree[2*k+1].sum+tree[2*k].rgss,tree[2*k+1].rgss);
	tree[k].gss=max(max(tree[2*k].gss,tree[2*k+1].gss),tree[2*k].rgss+tree[2*k+1].lgss);
}
mmp find(int k,int x,int y) 
{
	if(tree[k].l==x&&tree[k].r==y) {
		return tree[k];
	}
	int mid=(tree[k].l+tree[k].r)/2;
	if(y<=mid) return find(2*k,x,y);
	else if(x>mid) return find(2*k+1,x,y);
	else {
		mmp left,right,sum;
		left=find(2*k,x,mid);
		right=find(2*k+1,mid+1,y);
		sum.sum=left.sum+right.sum;
		sum.lgss=max(left.lgss,left.sum+right.lgss);
		sum.rgss=max(right.sum+left.rgss,right.rgss);
		sum.gss=max(max(left.gss,right.gss),left.rgss+right.lgss);
		return sum;
	}
}
int main()
{	while(cin>>n)
	{	build(1,1,n);
		cin>>m;
		for(int i=1;i<=m;i++) {
			cin>>x>>y;
			cout<<find(1,x,y).gss<<endl;
		}
	}
return 0;
}

如果你对这操作不理解的话,那可以自己举个栗子,然后按着函数自己在纸上或脑海里把函数遍历的流程走一遍,你就能明白其中的奥义了。 就是我懒得打字了

最后一道题,这道题挺复杂的,我这个菜鸟当时是向另一个大佬请教才写出来,但出了点小错误,寻找了几个小时才发现是少写了一个return (崩溃)!

ZBOJ 1858序列操作

Description
lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0 3 a b 询问[a, b]区间内总共有多少个1 4 a b 询问[a, b]区间内最多有多少个连续的1 对于每一种询问操作,lxhgww都需要给出回答,聪明的程序员们,你们能帮助他吗?

Input
输入数据第一行包括2个数,n和m,分别表示序列的长度和操作数目 第二行包括n个数,表示序列的初始状态 接下来m行,每行3个数,op, a, b,(0 < = op < = 4,0 < = a < = b)

Output
对于每一个询问操作,输出一行,包括1个数,表示其对应的答案

Sample Input
10 10
0 0 0 1 1 0 1 0 1 1
1 0 2
3 0 5
2 2 2
4 0 4
0 3 6
2 3 7
4 2 8
1 0 5
0 5 6
3 3 9
Sample Output
5
2
6
5

Hint
对于30%的数据,1<=n, m<=1000 对于100%的数据,1< = n, m < = 100000

#include<iostream>
#include<algorithm>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define ls 2*k
#define rs 2*k+1
using namespace std;
struct mmp
{
	int l,r;
	int f,pos;
	int sum,gss[2],lgss[2],rgss[2];
}tree[400005];
int n,m;
void build(int k,int l,int r)
{	tree[k].l=l;tree[k].r=r;
	tree[k].f=-1;tree[k].pos=0;
	if(l==r) {
		cin>>tree[k].sum ;
		tree[k].gss[0] = tree[k].lgss[0] = tree[k].rgss[0] = !tree[k].sum;
		tree[k].gss[1] = tree[k].lgss[1] = tree[k].rgss[1] = tree[k].sum;
		return ;
	}
	int mid=(l+r)/2;
	build(ls, l, mid); 
	build(rs, mid + 1, r);
	tree[k].sum = tree[ls].sum + tree[rs].sum;
 	for(int i = 0; i < 2; i++)
 	{
 		tree[k].gss[i] = max(max(tree[ls].gss[i], tree[rs].gss[i]), tree[ls].rgss[i] + tree[rs].lgss[i]);
 		tree[k].lgss[i] = (tree[ls].lgss[i] == tree[ls].r - tree[ls].l + 1) ? (tree[ls].lgss[i] + tree[rs].lgss[i]) : tree[ls].lgss[i];
 		tree[k].rgss[i] = (tree[rs].rgss[i] == tree[rs].r - tree[rs].l + 1) ? (tree[rs].rgss[i] + tree[ls].rgss[i]) : tree[rs].rgss[i];	
 	}
}
void change(mmp &u, int op)
{
	int len = u.r - u.l + 1;
	if(op < 2)
	{
		u.sum = op ? len : 0;
		u.gss[op] = u.lgss[op] = u.rgss[op] = len;
		u.gss[!op] = u.lgss[!op] = u.rgss[!op] = 0;
		u.f = op; u.pos = 0;
	}
	else
	{
		u.sum = len - u.sum;
		swap(u.gss[0], u.gss[1]);
		swap(u.lgss[0], u.lgss[1]);
		swap(u.rgss[0], u.rgss[1]);
		
		if(u.f == -1)	u.pos ^= 1;
		else	u.f ^= 1, u.pos = 0;
	}
}
void down(int k)
{
	if(!tree[k].pos && tree[k].f == -1)	return ;
	
	int op = tree[k].pos ? 3 : tree[k].f;
	change(tree[ls], op); change(tree[rs], op);
	tree[k].pos = 0; tree[k].f = -1;
}
void update(int k,int x,int y,int op)
{
	if(x <= tree[k].l && tree[k].r <= y)
	{
	    change(tree[k], op);
	    return ;
	}
	down(k);
	int mid = tree[k].l + tree[k].r >> 1;
	if(x <= mid)	update(ls, x, y, op);
	if(y > mid)	update(rs, x, y, op);
	tree[k].sum = tree[ls].sum + tree[rs].sum;
 	for(int i = 0; i < 2; i++)
 	{
 		tree[k].gss[i] = max(max(tree[ls].gss[i], tree[rs].gss[i]), tree[ls].rgss[i] + tree[rs].lgss[i]);
 		tree[k].lgss[i] = (tree[ls].lgss[i] == tree[ls].r - tree[ls].l + 1) ? (tree[ls].lgss[i] + tree[rs].lgss[i]) : tree[ls].lgss[i];
 		tree[k].rgss[i] = (tree[rs].rgss[i] == tree[rs].r - tree[rs].l + 1) ? (tree[rs].rgss[i] + tree[ls].rgss[i]) : tree[rs].rgss[i];	
 	}
}
mmp query(int k, int x, int y)
{
	if(x <= tree[k].l && tree[k].r <= y)	return tree[k];
	
	down(k);
	int mid = tree[k].l + tree[k].r >> 1;
	if(y <= mid)	return query(ls, x, y);
	else if(x > mid)	return query(rs, x, y);
	else
	{
		mmp left = query(ls, x, y), right = query(rs, x, y);
		mmp res;
		res.sum = left.sum + right.sum;
 	for(int i = 0; i < 2; i++)
 	{
 		res.gss[i] = max(max(left.gss[i],right.gss[i]), left.rgss[i] + right.lgss[i]);
 		res.lgss[i] = (left.lgss[i] == left.r - left.l + 1) ? (left.lgss[i] + right.lgss[i]) : left.lgss[i];
 		res.rgss[i] = (right.rgss[i] == right.r - right.l + 1) ? (right.rgss[i] + left.rgss[i]) : right.rgss[i];	
 	}
		return res;
	}
}
int main()
{	IOS;
	cin>>n>>m;
	build(1,1,n);
//	for(int i = 1; i <= 10; i++)
//		cout << query(1, i, i).sum << ' ';
//		cout << endl;
	int op,l,r;
	while(m--)
	{
		cin>>op>>l>>r;
		l++;r++;
		if(op < 3)
		{
		    update(1, l, r, op);
//		    for(int i = 1; i <= 10; i++)
//		        cout << query(1, i, i).sum << ' ';
//		       cout << endl;
		}
		else
		{
			mmp res = query(1, l, r);
			if(op == 3)	cout << res.sum << endl;
			else	cout << res.gss[1] << endl;
		}
	}
return 0;
}

补充:

tree[k].sum = tree[ls].sum + tree[rs].sum;
 	for(int i = 0; i < 2; i++)
 	{
 		tree[k].gss[i] = max(max(tree[ls].gss[i], tree[rs].gss[i]), tree[ls].rgss[i] + tree[rs].lgss[i]);
 		tree[k].lgss[i] = (tree[ls].lgss[i] == tree[ls].r - tree[ls].l + 1) ? (tree[ls].lgss[i] + tree[rs].lgss[i]) : tree[ls].lgss[i];
 		tree[k].rgss[i] = (tree[rs].rgss[i] == tree[rs].r - tree[rs].l + 1) ? (tree[rs].rgss[i] + tree[ls].rgss[i]) : tree[rs].rgss[i];	
 	}

代码里反复出现的这个代码,如果你嫌麻烦或觉得原代码很难看,可以把这个单独写成一个函数,比如:

void pushup(node &u, node l, node r)
{
 	u.sum = l.sum + r.sum;
 	for(int i = 0; i < 2; i++)
 	{
 		u.maxv[i] = max(max(l.maxv[i], r.maxv[i]), l.maxr[i] + r.maxl[i]);
 		u.maxl[i] = (l.maxl[i] == l.r - l.l + 1) ? (l.maxl[i] + r.maxl[i]) : l.maxl[i];
 		u.maxr[i] = (r.maxr[i] == r.r - r.l + 1) ? (r.maxr[i] + l.maxr[i]) : r.maxr[i];	
 	}
}

感谢那位教我这道题的zhao大佬!
如果有朝一日我有空闲的没事,我会对这个博客进行一点点补充;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值