#bzoj3186#单点修改的RMQ问题(zkw线段树版)

单点修改的RMQ问题

时间限制: 1 Sec  内存限制: 128 MB

题目描述

给出N(1 ≤ N ≤ 50,000)个数的序列A,下标从1到N,每个元素值均不超过INT_MAX。有两种操作:

(1)  Q i j:询问区间[i, j]之间的最大值与最小值的差值

(2) C i j:将A[i]增加j,j是一个整数。如果多次对同一个元素进行C操作,导致溢出,保留自然溢出后的结果。

一共有M (1 ≤ M ≤ 200,000) 次操作,对每个Q操作,输出一行,回答提问。

输入

第1行:2个整数N, M

第2行:N个元素

接下来M行,每行一个操作

输出

对每个Q操作,在一行上输出答案

样例输入

Copy (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

5 4
1 2 3 4 5
Q 2 4
C 1 2
C 2 1
Q 1 3

样例输出

2
0

此题可以用普通线段树做,但是今天新学了zkw线段树(请百度),然后就尝试用最普通的版本来做这道题,没有用差分思想(虽然同学们理解了但是我并不是很明白,所以等理解了再来补上)

zkw线段树将所有节点严格放在最下面的同一层叶子节点上,所以会有天然的数学关系来从下向上更新。

#include
#include
#include
using namespace std;
typedef long long LL;
const int Max=50000;
int N,T,M,X;
int Trx[4*Max+5],Trn[4*Max+5];
void build(){
	for(X=1; X<=N; X<<=1);
	--X;
	int MM=Max*4;
	for(int i=0; i<=MM; ++i)
		Trn[i]=0x3f3f3f,Trx[i]=-0x3f3f3f;
	for(int i=X+1; i<=X+N; ++i)
		scanf("%d",&Trx[i]),Trn[i]=Trx[i];
	for(int i=X; i; --i){
		Trx[i]=max(Trx[i<<1],Trx[i<<1|1]);
		Trn[i]=min(Trn[i<<1],Trn[i<<1|1]);
	}
}
void add(int p,int val){
	p+=X;
	Trx[p]+=val,Trn[p]+=val;
	for(p>>=1; p; p>>=1){
		Trx[p]=max(Trx[p<<1],Trx[p<<1|1]);
		Trn[p]=min(Trn[p<<1],Trn[p<<1|1]);
	}
}
int cnt(int s,int t){
	int ansx=-0x3f3f3f,ansn=0x3f3f3f;
	s+=X,t+=X;
	if(s==t)return Trx[s]-Trn[s];
	else ansx=max(Trx[s],Trx[t]),ansn=min(Trn[s],Trn[t]);
	for(; s^t^1; s>>=1,t>>=1){
		if(~s&1){
			ansx=max(ansx,Trx[s^1]);
			ansn=min(ansn,Trn[s^1]);
		}
		if(t&1){
			ansx=max(ansx,Trx[t^1]);
			ansn=min(ansn,Trn[t^1]);
		}
	}
	//printf("max=%d min=%d\n",ansx,ansn);
	return ansx-ansn;
}
int main(){
	scanf("%d%d",&N,&M);
	build();
	char ord[3];
	int a,b;
	for(int i=1; i<=M; ++i){
		scanf("%s",ord);
		if(ord[0]=='C'){
			scanf("%d%d",&a,&b);
			add(a,b);
		}
		else {
			scanf("%d%d",&a,&b);
			printf("%d\n",cnt(min(a,b),max(a,b)));
		}
	}
	return 0;
}




你可能感兴趣的:(线段树)