其他分享
首页 > 其他分享> > F. MEX Queries - 线段树 + 离散化

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

作者:互联网

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