求某个元素所在连续段的长度(也可求左右端点) (区间合并)

链接------->

题意:D代表破坏村庄,R代表修复最后被破坏的那个村庄,Q代表询问包括x在内的最大连续区间是多少
题解:线段树的合并,这道题就是需要合并,D的话破坏了这个村庄,那怎么办呢,就相当于中间切开,那么就是
相当于把当前这个点标记为0,然后再去更新它的父亲,这样怎么样会更加好做,好理解呢。
一段区间可以成为两段区间拼凑而成。
然后,我们定义老ls,rs表示左边连续区间,右边连续区间,ms表示最大连续区间,然后开始都为
整段,然后以D来切割,每段ls与rs最大值都不能超过当前这个点表示的区间
更新时方法:
左边就位左儿子的左边,右边就位右儿子的右边,然后最大值,就位左边,右边,和左儿子右边和
右儿子左边拼起来,就可以了。
查询操作:就是如果当前点,包涵在左边的右边,则可以和右子树的左儿子合并,同理,右边也一
样,然后最大值就是判断该段是否都联通了,是的话就直接可以推出返回值了,就

 

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cmath>
#include<string>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
struct node{
	ll l,r,lmx,rmx,mx;
}a[200000]; 
ll last[50001];//记录最后隧道被破坏的顺序 
//区间合并 (模板) 
void update(ll k){
	a[k].lmx=a[k<<1].lmx;
	if(a[k].lmx==a[k<<1].r-a[k<<1].l+1)
	a[k].lmx+=a[k<<1|1].lmx;
	a[k].rmx=a[k<<1|1].rmx;
	if(a[k].rmx==a[k<<1|1].r-a[k<<1|1].l+1)
	a[k].rmx+=a[k<<1].rmx;
	a[k].mx=max(max(a[k<<1].mx,a[k<<1|1].mx),a[k<<1].rmx+a[k<<1|1].lmx);
}
//build the xianduanshu
void build(ll k,ll l,ll r){
	a[k].l=l;a[k].r=r;
	a[k].lmx=a[k].rmx=a[k].mx=r-l+1;
	if(l==r)return ;
	ll mid=(a[k].l+a[k].r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	update(k);
}
//x表示要改变的隧道标号,cnt表示是破坏还是修复 
void change(ll k,ll x,ll cnt){
	if(a[k].l==a[k].r){
		if(cnt)
		a[k].lmx=a[k].rmx=a[k].mx=1;//修复 
		else a[k].lmx=a[k].rmx=a[k].mx=0;//破坏 
		return ;
	}
	ll mid=(a[k].l+a[k].r)>>1;
	if(x<=mid){
		change(k<<1,x,cnt);
	}
	else change(k<<1|1,x,cnt);
	update(k);
}
//难点:找包含标号x的最长连续区间长度 
ll query(ll k,ll x){
	//下面的返回条件特别注意
	//搜索到某个点,该区间没有连续的长度,该区间都连续-----符合任意一个都能找到答案 
	if(a[k].l==a[k].r||a[k].mx==0||a[k].mx==a[k].r-a[k].l+1){
		return a[k].mx;
	}
	ll mid=(a[k].l+a[k].r)>>1;
	if(x<=mid){
	if(x>=mid-a[k<<1].rmx+1){//说明可以与右孩子的左端点相连 
		//注意,右孩子的一定是mid+1,,因为只能说明有可能和右孩子的左端点相连 
		return query(k<<1,x)+query(k<<1|1,mid+1);//坑点 
	} 
	else return query(k<<1,x);
	}
	else {//同理 
		if(x<=mid+a[k<<1|1].lmx){
			return query(k<<1,mid)+query(k<<1|1,x);
		}
		else return query(k<<1|1,x);
	}
}

int main(){
	ll n,m;
	while(cin>>n>>m){
		build(1,1,n);
		string s;
		ll x,cnt=0;
		while(m--){
			cin>>s;
			if(s=="D"){
				cin>>x;
				last[++cnt]=x;
				change(1,x,0);
			}
			else if(s=="R"){
				if(cnt==0)continue;
				change(1,last[cnt],1);
				cnt--;
			}
			else {
				cin>>x;
				cout<<query(1,x)<<endl;
			}
		}
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值