线段树模板

首先是建树:

const int MAXN = 1005;//定义 MAXM 为线段最大长度

int Tree[4*MAXN];// Data数组为 main 函数中读入的内容,Tree数组为需要查询的数的信息(如和、最值等),树的空间大小为线段最大长度的四倍!四倍!四倍! 
int Data[MAXN];

void Build(int temp,int left,int right){//传入的参数为 temp:当前需要建立的结点;left:当前需要建立的左端点;right:当前需要建立的右端点。 
	if(left==right){//当左端点等于右端点即建立叶子结点时,直接给数组信息赋值
		Tree[temp] = Data[left];
		return ;
	}
	int mid = left + (right-left)/2; // mid为中间点,左儿子结点为 [left,mid] ,右儿子结点为 [mid+1,right];
	Build(temp<<1,left,mid);
	Build(temp<<1|1,mid+1,right);
	Tree[temp] = max(Tree[temp<<1],Tree[temp<<1|1]);//递归返回时用儿子结点更新父节点,此处可进行更新最大值、最小值、区间和等操作
}

单点修改以及对应的区间查询:

void Updata(int temp,int left,int right,int tempt,int value){//tempt为需要修改的叶子结点左端点,value为需要修改成的值;
	if(left == right){
		Tree[temp] = value;
		return ;
	}
	int mid = left + (right-left)/2;
	if(tempt<=mid)Updata(temp<<1,left,mid,tempt,value);//若需要更新的叶子结点在当前结点的左儿子结点的范围内,则递归更新左儿子结点
	else Updata(temp<<1|1,mid+1,right,tempt,value);//否则更新右儿子结点
	Tree[temp] = max(Tree[temp<<1],Tree[temp<<1|1]);//递归回之后用儿子结点更新父节点(此处是区间最大值)
}

int query(int temp,int left,int right,int ql,int qr){//区间查询 
	if(qr<left || ql>right)return -1;//若当前结点和需要查找的区间不相交,则返回一个对于区间查询无关的值(如求和时返回0,求最大值时返回-1等)
	if(ql<=left && qr>=right)return Tree[temp]; //若当前结点的区间被需要查询的区间覆盖,则返回当前结点的信息
	int mid = left + (right-left)/2;
	return max(query(temp<<1,left,mid,ql,qr),query(temp<<1|1,mid+1,right,ql,qr));
} 

区间修改以及相应的查询:

区间修改用到了lazy的思想,即当一个区间需要更新时,只递归更新到那一层结点,并将其下层结点所需要更新的信息保存在数组中,然后返回,只有当下次遍历到那个结点(更新过程中或查询过程中),才将那个结点的修改信息传递下去,这样就避免了区间修改的每个值的修改

区间加值:

int add[4*MAXN];//记录点是否有更新信息。 

void Pushdown(int temp,int left,int right){//Pushdown函数,将temp结点的信息传递到左右子节点上
	add[temp<<1] += add[temp]; //左右儿子结点均加上父节点的更新值
	add[temp<<1|1] += add[temp];
	int mid = left + (right-left)/2;
	Tree[temp<<1] += add[temp]*(mid-left+1);//左右儿子结点均按照需要加的值总和更新结点信息
	Tree[temp<<1|1] += add[temp]*(right-mid);
	add[temp] = 0;//信息传递完之后就可以将父节点的更新信息删除
}

void Updata(int temp,int left,int right,int ql,int qr,int value){//ql、qr为需要更新的区间左右端点,value为需要增加的值。 
	if(ql<=left && qr>=right){//与单点更新一样,当当前结点被需要更新的区间覆盖时
		add[temp] += value; //更新该结点的更新信息
		Tree[temp] += value*(right-left+1);//更新该结点信息
		return ;//根据lazy思想,由于不需要遍历到下层结点,因此不需要继续向下更新,直接返回
	} 
	
	if(add[temp])Pushdown(temp,left,right);
	int mid = left + (right-left)/2;
	if(ql<=mid)Updata(temp<<1,left,mid,ql,qr,value);//当需更新区间在当前结点的左儿子结点内,则更新左儿子结点
	if(qr>mid)Updata(temp<<1|1,mid+1,right,ql,qr,value); //当需更新区间在当前结点的右儿子结点内,则更新右儿子结点
	Tree[temp] = Tree[temp<<1]+Tree[temp<<1|1];//递归回上层时一步一步更新回父节点
}

long long query(int temp,int left,int right,int ql,int qr){ //ql、qr为需要查询的区间
	if(ql<=left && qr>=right)return Tree[temp];//若当前结点覆盖区间即为需要查询的区间,则直接返回当前结点的信息
	if(add[temp])PushDown(temp,left,right); //将当前结点的更新信息传递给其左右子节点
	int mid = left + (right-left)/2;
	long long ans = 0;						//所需查询的结果
	if(ql<=m)ans += query(temp<<1,left,mid,ql,qr); //若所需查询的区间与当前结点的左子节点有交集,则结果加上查询其左子节点的结果
	if(qr>m)ans += query(temp<<1|1,mid+1,right,ql,qr);//若所需查询的区间与当前结点的右子节点有交集,则结果加上查询其右子节点的结果
	return ans;
}

区间改值(其实只是Pushdow函数和Update中修改部分与区间加值不同,并多了一个判断数组):

int Change[4*MAXN];//记录节点改变信息 
bool book[4*MAXN];//记录节点是否有改变要传递 

void Pushdown(int temp,int left,int right){//Pushdown和区间加值不同,改值时修改结点信息只需要对修改后的信息求和即可,不用加上原信息
	Change[temp<<1] = Change[temp];
	Change[temp<<1|1] = Change[temp];
	book[temp<<1] = true;
	book[temp<<1|1] = true;
	book[temp] = false;
	int mid = left + (right-left)/2;
	Tree[temp<<1] = Change[temp]*(mid-left+1);
	Tree[temp<<1|1] = Change[temp]*(right-mid);
}

void Updata(int temp,int left,int right,int ql,int qr,int value){
	if(ql<=left && qr>=right){				 //同样更新结点信息和区间加值不同
		Change[temp] = value;
		book[temp] = true;
		Tree[temp] = value*(right-left+1);
		return;
	}
	
	if(book[temp])Pushdown(temp,left,right);
	
	int mid = left + (right-left)/2;
	if(ql<=mid)Updata(temp<<1,left,mid,ql,qr,value);
	if(qr>mid)Updata(temp<<1|1,mid+1,right,ql,qr,value);
	
	Tree[temp] = Tree[temp<<1]+Tree[temp<<1|1]; 
} 

long long query(int temp,int left,int right,int ql,int qr){
	if(ql<=left && qr>=right)return Tree[temp];
	if(book[temp])Pushdown(temp,left,right);
	int mid = left + (right-left)/2;
	long long ans = 0;
	if(ql<=mid)ans += query(temp<<1,left,mid,ql,qr);
	if(qr>mid)ans += query(temp<<1|1,mid+1,right,ql,qr);
	return ans;
}

区间合并:

const int MAXN = 100005;

int Data[MAXN];
int Change[MAXN*4];

struct D{
	int l;//从左边界点开始的最大连续 
	int r;//从右边界点开始的最大连续
	int maxn;//区间的最大连续(端点是左或右或左右边界点或没有)
	int s;//区间的长度。 
}Tree[MAXN*4];

void Up(int temp){
	Tree[temp].l = Tree[temp<<1].l;
	if(Tree[temp<<1].l == Tree[temp<<1].s)Tree[temp].l += Tree[temp<<1|1].l;
	Tree[temp].r = Tree[temp<<1|1].r;
	if(Tree[temp<<1|1].r == Tree[temp<<1|1].s)Tree[temp].r += Tree[temp<<1].r;
	Tree[temp].maxn = max(Tree[temp<<1].maxn,Tree[temp<<1|1].maxn);
	Tree[temp].maxn = max(Tree[temp].maxn,Tree[temp<<1].r+Tree[temp<<1|1].l);
}

void Build(int temp,int left,int right){
	Tree[temp].s = right-left+1;
	if(left == right){
		if(Data[left]){		/*这里一般是两种状态,具体根据题来改*/
			Tree[temp].l = Tree[temp].r = Tree[temp].maxn = 1;
		}else {
			Tree[temp].l = Tree[temp].r = Tree[temp].maxn = 0;
		}
		return ;
	}
	int mid = left + (right-left)/2;
	Build(temp<<1,left,mid);
	Build(temp<<1|1,mid+1,right);
	Up(temp);
}

void PushDown(int temp){
	if(Change[temp]){
		/*根据题要求写*/
	}
}

void Updata(int temp,int left,int right,int ql,int qr){
	if(ql<=left && qr>=right){
		/*根据题要求写*/
		return ;
	}
	
	PushDown(temp);
	
	int mid = left + (right-left)/2;
	if(ql<=mid)Updata(temp<<1,left,mid,ql,qr);
	if(qr>mid)Updata(temp<<1|1,mid+1,right,ql,qr);
	
	Up(temp);
} 

int query(int temp,int left,int right,int ql,int qr){
	if(ql==left && qr==right)return Tree[temp].maxn;
	
	PushDown(temp);
	
	int mid = left + (right-left)/2;
	if(qr<=mid)return query(temp<<1,left,mid,ql,qr);
	else if(ql>mid)return query(temp<<1|1,mid+1,right,ql,qr);
	else {
		int a,b,c;
		a = query(temp<<1,left,mid,ql,mid);
		b = query(temp<<1|1,mid+1,right,mid+1,qr);
		c = min(mid-ql+1,Tree[temp<<1].r) + min(qr-mid,Tree[temp<<1|1].l);
		return max(a,max(b,c));
	}
}

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值