MEX Queries(线段树离散化)

CodeForces - 817F MEX Queries

题意:给定一个初始全为 0 0 0 的串,每次对于区间 [ l , r ] [l, r] [l,r],有三种操作:

1 - add:将区间置1
2 - remove:将区间置0
3 - invert:将区间 01 翻转

每次操作完输出 M E X MEX MEX

由于 1 ≤ l , r ≤ 1 0 18 1\leq l , r\leq10^{18} 1l,r1018, 考虑离散化,需要将 1 , l , r , r + 1 1,l,r,r+1 1,l,r,r+1离散,这些点都可能是答案

ll n, m, cnt;
//用 b数组来维护离散化后的结果
ll b[maxn * 3];

struct Node{
	ll op, l, r;
}a[maxn];

struct node{
	ll sum, l, r, lazy, lazy1;
	//lazy = 0 或 1, 0表示不需要翻转,1表示需要翻转
	//lazy1 = -1 或 0 或 1, -1表示子区间没有置 0或置 1操作, 0表示子区间有置 0, 1表示子区间有置 1操作
}tree[maxn * 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].lazy1 == 1)	//若有置 1操作,区间和为区间长度,若本身有翻转操作,会被置 1操作覆盖,所以lazy = 0
	{
		tree[p].lazy1 = -1;
		tree[p << 1].sum = tree[p << 1].r - tree[p << 1].l + 1;
		tree[p << 1].lazy = 0;
		tree[p << 1].lazy1 = 1;
		tree[p << 1 | 1].sum = tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1;
		tree[p << 1 | 1].lazy = 0;
		tree[p << 1 | 1].lazy1 = 1;
	}
	else if (!tree[p].lazy1) //若有置 0操作,全部为 0,同理翻转操作会被覆盖
	{
		tree[p].lazy1 = -1;
		tree[p << 1].sum = 0;
		tree[p << 1].lazy1 = 0;
		tree[p << 1].lazy = 0;
		tree[p << 1 | 1].sum = 0;
		tree[p << 1 | 1].lazy1 = 0;
		tree[p << 1 | 1].lazy = 0;
	}
	else if (tree[p].lazy) //如果没有置 0或置 1操作,但有翻转操作
	{
		tree[p].lazy = 0;
		//因为取反,原来是 0现在是1,原来是 1现在是 0,所以区间和等于长度 - 原区间和
		tree[p << 1].sum = tree[p << 1].r - tree[p << 1].l + 1 - tree[p << 1].sum;
		//若子区间有置 0或置 1操作, lazy1取反
		if (tree[p << 1].lazy1 != -1)  
			tree[p << 1].lazy1 ^= 1;
		//子区间没有置 0或置 1操作,子区间 lazy取反
		else 
			tree[p << 1].lazy ^= 1;
		tree[p << 1 | 1].sum = tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1 - tree[p << 1 | 1].sum;
		if (tree[p << 1 | 1].lazy1 != -1)
			tree[p << 1 | 1].lazy1 ^= 1;
		else
			tree[p << 1 | 1].lazy ^= 1;
	}
}

void add(ll ql, ll qr, ll p)
{
	ll l = tree[p].l;
	ll r = tree[p].r;
	if (ql <= l && qr >= r)
	{
		tree[p].sum = r - l + 1;
		tree[p].lazy = 0;
		tree[p].lazy1 = 1;
		return;
	}
	push_down(p);
	ll mid = (l + r) >> 1;
	if (ql <= mid)
		add(ql, qr, p << 1);
	if (qr > mid)
		add(ql, qr, p << 1 | 1);
	push_up(p);
}

void rem(ll ql, ll qr, ll p)
{
	ll l = tree[p].l;
	ll r = tree[p].r;
	if (ql <= l && qr >= r)
	{
		tree[p].sum = 0;
		tree[p].lazy = 0;
		tree[p].lazy1 = 0;
		return;
	}
	push_down(p);
	ll mid = (l + r) >> 1;
	if (ql <= mid)
		rem(ql, qr, p << 1);
	if (qr > mid)
		rem(ql, qr, p << 1 | 1);
	push_up(p);
}

void invert(ll ql, ll qr, ll p)
{
	ll l = tree[p].l;
	ll r = tree[p].r;
	if (ql <= l && qr >= r)
	{
		tree[p].sum = r - l + 1 - tree[p].sum;
		if (tree[p].lazy1 != -1)
			tree[p].lazy1 ^= 1;
		else
			tree[p].lazy ^= 1;
		return;
	}
	push_down(p);
	ll mid = (l + r) >> 1;
	if (ql <= mid)
		invert(ql, qr, p << 1);
	if (qr > mid)
		invert(ql, qr, p << 1 | 1);
	push_up(p);
}

ll query(ll p)
{
	ll l = tree[p].l;
	ll r = tree[p].r;
	if (l == r)
		return b[l];
	push_down(p);
	//若区间和小于区间长度,说明有未出现的点
	if (tree[p << 1].sum < tree[p << 1].r - tree[p << 1].l + 1) 
		return query(p << 1);
	return query(p << 1 | 1);
}

void build(ll l, ll r, ll p)
{
	tree[p].l = l;
	tree[p].r = r;
	tree[p].sum = 0;
	tree[p].lazy = 0;
	tree[p].lazy1 = -1;
	if (l == r)
		return;
	int mid = (l + r) >> 1;
	build(l, mid, p << 1);
	build(mid + 1, r, p << 1 | 1);
}

void solve()
{
	cin >> n;
	//离散化, 1, l, r, r + 1都需要被离散化
	b[++cnt] = 1;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i].op >> a[i].l >> a[i].r;
		b[++cnt] = a[i].l;
		b[++cnt] = a[i].r;
		b[++cnt] = a[i].r + 1;
	}
	sort(b + 1, b + 1 + cnt);
	ll len = unique(b + 1, b + 1 + cnt) - (b + 1);
	build(1, len, 1);
	for (int i = 1; i <= n; i++)
	{
		ll l = lower_bound(b + 1, b + 1 + len, a[i].l) - b;
		ll r = lower_bound(b + 1, b + 1 + len, a[i].r) - b;
		if (a[i].op == 1)
			add(l, r, 1);
		else if (a[i].op == 2)
			rem(l, r, 1);
		else
			invert(l, r, 1);
		cout << query(1) << endl;
	}
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值