2021台湾-Flip-(线段树区间合并的理解)

F

题意:
就是给你一个01数组,然后给你m次操作,每次要么是让[l,r]这段区间的0和1都翻转,要么是查询[l,r]这段区间有多少好的子区间。好的区间定义为,这个区间的0和1是间隔的,也就是没有连续>=2个的0或者1。

思考:

  1. 当时思考来思考去,就感觉不太好操作,因为感觉线段树不知道咋维护答案,害,还是对线段树的区间合并理解的不透彻。
  2. 其实就正常的去维护,只要到L>=l&&R<=r的时候,线段树里的东西就要修改,然后如果是区间合并的话,在pushup的时候,这个节点就由两个儿子来合并出来就可以了。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;

int T,n,m,k;
int va[N];

struct seg_tree{
	#define node_l node<<1
	#define node_r node<<1|1
	struct Node{
		int L,R,sum,laz;
		int lop,rop;
		int lsum,rsum;
	}t[4*N];
	void pushdown(int node)
	{
		if(!t[node].laz) return ;
		t[node_l].laz ^= 1;
		t[node_l].lop ^= 1;t[node_l].rop ^= 1;
		t[node_r].laz ^= 1;
		t[node_r].lop ^= 1;t[node_r].rop ^= 1;
		t[node].laz = 0;
	}
	void pushup(int node) //当前node由两个儿子合并
	{
		t[node].lop = t[node_l].lop;
		t[node].rop = t[node_r].rop;
		t[node].sum = t[node_l].sum+t[node_r].sum;
		t[node].lsum = t[node_l].lsum;
		t[node].rsum = t[node_r].rsum;
		if(t[node_l].rop!=t[node_r].lop)
		{
			t[node].sum += t[node_l].rsum*t[node_r].lsum;
			if(t[node_l].lsum==t[node_l].R-t[node_l].L+1)
			t[node].lsum += t[node_r].lsum;
			if(t[node_r].rsum==t[node_r].R-t[node_r].L+1)
			t[node].rsum += t[node_l].rsum;
		}
	}
	void build(int node,int l,int r)
	{
		t[node].L = l,t[node].R = r;
		if(l==r)
		{
			t[node].sum = 1;
			t[node].lop = t[node].rop = va[l];
			t[node].lsum = t[node].rsum = 1;
			return ;
		}
		int mid = (l+r)>>1;
		build(node_l,l,mid);build(node_r,mid+1,r);
		pushup(node);
	}
	void update(int node,int l,int r)
	{
		if(t[node].L>=l&&t[node].R<=r) //需要修改的就这些,其他的都是不变的。
		{
			t[node].laz ^= 1;
			t[node].lop ^= 1;t[node].rop ^= 1;
			return ;
		}
		pushdown(node);
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) update(node_l,l,r);
		else if(l>mid) update(node_r,l,r);
		else update(node_l,l,mid),update(node_r,mid+1,r);
		pushup(node);
	}
	Node query(int node,int l,int r)
	{
		if(t[node].L>=l&&t[node].R<=r) return t[node];
		pushdown(node);
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) return query(node_l,l,r);
		else if(l>mid) return query(node_r,l,r);
		else //查询答案的时候注意,如果分成了两个区间需要把两个区间合并起来,再返回。
		{
			Node now;
			Node tl = query(node_l,l,mid);
			Node tr = query(node_r,mid+1,r);
			now.L = tl.L,now.R = tr.R;
			now.lop = tl.lop,now.rop = tr.rop;
			now.sum = tl.sum+tr.sum;
			now.lsum = tl.lsum,now.rsum = tr.rsum;
			if(tl.rop!=tr.lop)
			{
				now.sum += tl.rsum*tr.lsum;
				if(tl.lsum==tl.R-tl.L+1) now.lsum += tr.lsum;
				if(tr.rsum==tr.R-tr.L+1) now.rsum += tl.rsum;
			}
			return now;
		}
		pushup(node);
	}
}tsum;

signed main()
{
	IOS;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>va[i];
	tsum.build(1,1,n);
	while(m--)
	{
		int op,a,b;
		cin>>op>>a>>b;
		if(op==1) tsum.update(1,a,b);
		else cout<<tsum.query(1,a,b).sum<<"\n";
	}
	return 0;
}

牛客月赛-最好的宝石

题意:
就是给你一个数组,然后又m次操作,每次操作要么是让第x个数变成y,要么是查询[l,r]区间最大值是几,并且有多少个。

思考:

  1. 这个也是经典的区间合并,修改的时候,如果跨区间了也要pushup,当前节点的答案由两个儿子处理出来。然后查询的时候也是,如果跨区间了,那么就要把两个区间的答案合并起来。

代码:

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define db double
#define int long long
#define PII pair<int,int >
#define mem(a,b) memset(a,b,sizeof(a))
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);

using namespace std;
const int mod = 1e9+7,inf = 1e18;
const int N = 2e5+10,M = 2010;

int T,n,m,k;
int va[N];

struct set_tree{
	#define node_l node<<1
	#define node_r node<<1|1
	struct node
	{
		int L,R;
		int maxn;
		int sum;
	}t[4*N];
	void build(int node,int l,int r)
	{
		t[node].L = l,t[node].R = r;
		if(l==r)
		{
			t[node].maxn = va[l];
			t[node].sum = 1;
			return ;
		}
		int mid = l+r>>1;
		build(node_l,l,mid);
		build(node_r,mid+1,r);
		t[node].maxn = max(t[node_l].maxn,t[node_r].maxn);
		if(t[node_l].maxn == t[node].maxn) t[node].sum += t[node_l].sum;
		if(t[node_r].maxn == t[node].maxn) t[node].sum += t[node_r].sum;
	}	
	void change(int node,int x,int value)
	{
		if(t[node].L>x||t[node].R<x) return ;
		if(t[node].L==x&&t[node].R==x)
		{
			t[node].maxn = value;
			t[node].sum = 1;
			return ;
		}
		change(node_l,x,value);
		change(node_r,x,value);
		t[node].maxn = max(t[node_l].maxn,t[node_r].maxn);
		t[node].sum = 0;
		if(t[node_l].maxn == t[node].maxn) t[node].sum += t[node_l].sum;
		if(t[node_r].maxn == t[node].maxn) t[node].sum += t[node_r].sum;
	}
	PII query(int node,int l,int r)
	{
		if(t[node].L>=l&&t[node].R<=r)
		{
			return {t[node].maxn,t[node].sum};
		}
		int mid = (t[node].L+t[node].R)>>1;
		if(r<=mid) return query(node_l,l,r);
		else if(l>mid) return query(node_r,l,r);
		else
		{
			auto t1 = query(node_l,l,r),t2 = query(node_r,l,r);
			if(t1.fi==t2.fi) return {t1.fi,t1.se+t2.se};
			else if(t1.fi>t2.fi) return t1;
			else return t2;
		}
	}
}tsum;

signed main()
{
	IOS;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>va[i];
	tsum.build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		string op;
		int a,b;
		cin>>op>>a>>b;
		if(op=="Ask")
		{
			auto now = tsum.query(1,a,b);
			cout<<now.fi<<" "<<now.se<<"\n";
		}
		else tsum.change(1,a,b);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值