ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

F. MEX Queries - 线段树 + 离散化

2022-05-03 12:35:21  阅读:145  来源: 互联网

标签:lazy xl 线段 tree tot MEX Queries 区间 ll


F. MEX Queries

题意

1 l r 将区间l r 置 1
2 l r 将区间l r 置 0
3 l r 翻转区间l r(即 是1置0 是0置1)
求每次操作后值是0的最左位置编号

思路

用线段树维护 区间和
用一个lazy 标记当前结点 置1 置0 翻转 或者 无需操作(用于减少时间复杂度)
对于翻转操作 每次更新一个结点的lazy要判断之前lazy是什么 然后根据之前那个lazy和新赋的lazy给出新的lazy
置零和置一操作直接更新lazy即可
更新区间和:
置零:tree[i].sum = 0;
置一:tree[i].sum = tree[i].r - tree[i].l + 1;(区间长即区间和)
翻转:tree[i].sum = tree[i].r - tree[i].l + 1 - tree[i].sum;(区间长减去原来的区间和即新的区间和)
最后询问整个区间 query时要下放lazy(要访问就必须下放lazy的信息 为访问到就暂时不用下放信息)
离散化:
因为给的数据l r会很大可达1e18(肯定tle)而操作次数只有1e5故要离散化
每次的答案只可能是给出的l 或 r+1 或 1 所以只要把l, r+1和 1放入用于离散化的数组 因为r时区间边界要用于查询更新等操作所以也要放入数组中
然后数组排序后离散化去重 将l r在离散化数组中的位置区间当作对应区间操作即可

#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const ll N = 1e18 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
ll n, op[M], l[M], r[M], a[M * 3], b[M * 3];//一定要开三倍大小 因为每次都要放进去 l r r+1
map<ll, ll>mp;

struct node {
	ll l, r, sum, lazy;
}tree[M * 12];//一般线段树要开4倍 这里要再乘以3(l r r+1) 所以是12倍

void push_up(ll p) {
	tree[p].sum = tree[p << 1].sum + tree[p << 1|1].sum;
}
//下方操作
void push_down(ll p) {
	if (tree[p].lazy == -1) return;
	if (tree[p].lazy == 1) {//如果父节点lazy是置1 左右儿子的lazy直接是置1
		tree[p << 1].lazy = tree[p].lazy;
		tree[p << 1 | 1].lazy = tree[p].lazy;
		tree[p << 1].sum = tree[p << 1].r - tree[p << 1].l + 1;
		tree[p << 1 | 1].sum = tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1;
	}
	else if (tree[p].lazy == 0) {//如果父节点lazy是置0 左右儿子的lazy直接是置0
		tree[p << 1].lazy = tree[p].lazy;
		tree[p << 1 | 1].lazy = tree[p].lazy;
		tree[p << 1].sum = tree[p << 1 | 1].sum = 0;
	}
	else {//如果父节点lazy是翻转 要判断儿子结点之前的lazy标记再相应翻转
                //每次都先把把儿子结点的sum值更新一下 不然会出现多个lazy值重叠而sum值却迟迟没更新 
		tree[p << 1].sum = tree[p << 1].r - tree[p << 1].l + 1 - tree[p << 1].sum;
		tree[p << 1|1].sum = tree[p << 1|1].r - tree[p << 1|1].l + 1 - tree[p << 1|1].sum;
		ll x = tree[p << 1].lazy;
		if (x == 0) tree[p << 1].lazy = 1;//之前lazy是0 就标记成1
		else if (x == 1) tree[p << 1].lazy = 0;//之前lazy是1 就标记成0
		else if (x == 3) tree[p << 1].lazy = -1;//之前lazy是翻转 就不用变
		else 	tree[p << 1].lazy = 3;//之前lazy是未标记 就标记成翻转
                
		x = tree[p << 1 | 1].lazy;
		if (x == 0) tree[p << 1 | 1].lazy = 1;
		else if (x == 1) tree[p << 1 | 1].lazy = 0;
		else if (x == 3) tree[p << 1 | 1].lazy = -1;
		else tree[p << 1 | 1].lazy = 3;
		
	}
    tree[p].lazy = -1;//下放父亲节点后 lazy置为未标记
}

void build(ll l, ll r, ll p) {
	tree[p].l = l; tree[p].r = r;
	tree[p].lazy = -1;//放在if外面 每个结点的lazy都要初始化为-1
	if (l == r) {
		tree[p].sum = 0;
		return;
	}
	ll mid = (l + r) / 2;
	build(l, mid, p << 1);
	build(mid + 1, r, p << 1 | 1);
	push_up(p);
}

void update1(ll xl, ll xr, ll p) {
	ll l = tree[p].l, r = tree[p].r;
	if (l >= xl && r <= xr) {
		tree[p].sum = r - l + 1;
		tree[p].lazy = 1;
		return;
	}
	push_down(p);//更新完一个结点就下放一下信息
	ll mid = (l + r) / 2;
	if (mid >= xl) update1(xl, xr, p << 1);
	if (mid < xr) update1(xl, xr, p << 1 | 1);
	push_up(p);
}

void update2(ll xl, ll xr, ll p) {
	ll l = tree[p].l, r = tree[p].r;
	if (l >= xl && r <= xr) {
		tree[p].sum = 0;
		tree[p].lazy = 0;
		return;
	}
	push_down(p);
	ll mid = (l + r) / 2;
	if (mid >= xl) update2(xl, xr, p << 1);
	if (mid < xr) update2(xl, xr, p << 1|1);
	push_up(p);
}

void update3(ll xl, ll xr, ll p) {
	ll l = tree[p].l, r = tree[p].r;
	if (l >= xl && r <= xr) {
          //先更新sum避免后面没更新 
        tree[p].sum = tree[p].r - tree[p].l + 1 - tree[p].sum;
                //对于lazy更新要结合之前的lazy标记
		if (tree[p].lazy == 0) tree[p].lazy = 1;
		else if (tree[p].lazy == 1) tree[p].lazy = 0;
		else if (tree[p].lazy == -1) tree[p].lazy = 3;
		else tree[p].lazy = -1;
		return;
	}
	push_down(p);
	ll mid = (l + r) / 2;
	if (mid >= xl) update3(xl, xr, p << 1);
	if (mid < xr) update3(xl, xr, p << 1|1);
	push_up(p);
}

ll query(ll p) {
	ll l = tree[p].l, r = tree[p].r;
	if (l == r) return b[l];
	push_down(p);
	if (tree[p << 1].sum < (l + r) / 2 - l + 1) return query(p << 1);
	else return query(p << 1|1);
}

void solve()
{
	cin >> n;
	ll tot = 0;
	for (int i = 1; i <= n; i++) {
		cin >> op[i] >> l[i] >> r[i];
		b[++tot] = l[i];
		b[++tot] = r[i];
		b[++tot] = r[i] + 1;
	}
	b[++tot] = 1;
	sort(b + 1, b + 1 + tot);//排序
	ll len = unique(b + 1, b + (ll)1 + tot) - b - 1;//离散化去重
	for (int i = 1; i <= len; i++) //离散化对应
	    mp[b[i]] = i;

	build(1, len, 1);
	for (int i = 1; i <= n; i++) {
		if (op[i] == 1) update1(mp[l[i]], mp[r[i]], 1);
		else if (op[i] == 2) update2(mp[l[i]], mp[r[i]], 1);
		else update3(mp[l[i]], mp[r[i]], 1);
		cout << query(1) << "\n";
	}
}

signed main()
{
	IOS;
	int t = 1;
	//cin >> t;
	while (t--)
	{
		solve();
	}
}

标签:lazy,xl,线段,tree,tot,MEX,Queries,区间,ll
来源: https://www.cnblogs.com/yaqu-qxyq/p/16218075.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有