Tunnel Warfare

36 篇文章 0 订阅
20 篇文章 0 订阅

传送门

N N N的数组,初始为 1 1 1

支持三种操作

  • 将下标为 x x x 的值变为 0 0 0
  • 求与下标 x x x​ 相连的连续的 1 1 1 的​长度
  • 恢复上一次改为 0 0 0 的操作

分析

线段树维护值01序列

这道题和【Hotel】有点相关

此题要维护x左边相连最远多少 + 右边最远多少

那么我们就维护左端点和右端点最远的距离!

l l l​​ 为区间左端点,能预见的最远到右边连续第一个 0 0 0​​​的位置

r r r​为区间右端点,能预见的最远到左边连续第一个 0 0 0​的位置(连续最后一个 1 1 1​不就是前一个嘛)

假设区间长度为1,初始化 l = N + 1 l=N+1 l=N+1 r = 0 r=0 r=0,这里就能知道,与之相连的连续 1 1 1的长度为 l − r − 1 l-r-1 lr1

注意:(不在区间,就认为都是1,反正看不见,我往上的父节点能够看见,让父亲节点来更新)

假设区间长度为2,那么从左边知道 t r [ l s ] . l , t r [ l s ] . r tr[ls].l,tr[ls].r tr[ls].l,tr[ls].r 右边知道 t r [ r s ] . l , t r [ r s ] . r tr[rs].l, tr[rs].r tr[rs].l,tr[rs].r

此时区间能够预见左边到多远,右边到多远,此时更新能预见的 l l l r r r

  • l = min ⁡ ( t r [ l s ] . l , t r [ r s ] . l ) l = \min(tr[ls].l, tr[rs].l) l=min(tr[ls].l,tr[rs].l)

  • r = m a x ( t r [ l s ] . r , t r [ r s ] . r ) r = max(tr[ls].r,tr[rs].r) r=max(tr[ls].r,tr[rs].r)

这样就能保证从区间 1 1 1推到区间 2 2 2了,由此能够推向区间 4 4 4,信息往上走正确性显然

现在拥有这些信息如何确定某个点连续的大小呢?

对于某个点 x x x来说,我们要知道它连续的点,就要知道 x x x 左边第一个 0 0 0的位置,右边第一个 0 0 0的位置

那么我们可以直接查 x x x l l l r r r不就得了?

但是 x x x​​是不能预见左边和右边情况的,它是默认它管理的区间以外都是1

那么谁能够准确预见 x x x的左边情况,和右边情况呢?答案是管理区间 [ 1 , x ] [1, x] [1,x] [ x , N ] [x, N] [x,N] 的节点

[ 1 , x ] [1, x] [1,x] 能够准确知道 r r r 的情况, [ x , N ] [x,N] [x,N]能够知道 l l l​ 的情况,查询这个得到答案为 l − r − 1 l-r-1 lr1

如果 l = = r l==r l==r 的话,说明什么? x x x 就是0,所以答案是 0 0 0

如果要恢复的话,用个栈存一下操作,恢复栈顶操作就行了

不知道有没有说清楚,以下是代码,看代码理解会更轻松

代码

//446928H
/*
  @Author: YooQ
*/
#include <bits/stdc++.h>
using namespace std;
#define sc scanf
#define pr printf
#define ll long long
#define int long long
#define FILE_OUT freopen("out", "w", stdout);
#define FILE_IN freopen("in", "r", stdin);
#define debug(x) cout << #x << ": " << x << "\n";
#define AC 0
#define WA 1
#define INF 0x3f3f3f3f
const ll MAX_N = 1e6+5;
const ll MOD = 1e9+7;
int N, M, K;

struct Tr {
	int l, r;
	
	Tr operator + (const Tr& B) const {
		Tr res;
		res.l = min(l, B.l);
		res.r = max(r, B.r);
		return res;
	}
}tr[MAX_N<<2];

void push_up(int rt) {
	int lc = rt << 1;
	int rc = lc|1;
	tr[rt].l = min(tr[lc].l, tr[rc].l);
	tr[rt].r = max(tr[lc].r, tr[rc].r);
}

void build(int rt, int l, int r) {
	if (l == r) {
		tr[rt].l = N+1;
		tr[rt].r = 0;
		return;
	}
	int mid = l + ((r-l)>>1);
	build(rt<<1, l, mid);
	build(rt<<1|1, mid+1, r);
	push_up(rt);
}

void update(int rt, int l, int r, int x, int k) {
	if (l == r) {
		if (!k) {
			tr[rt].r = tr[rt].l = x;
		} else {
			tr[rt].l = N+1;
			tr[rt].r = 0;
		}
		return;
	}
	int mid = l + ((r-l)>>1);
	if (x <= mid) update(rt<<1, l, mid, x, k);
	if (x  > mid) update(rt<<1|1, mid+1, r, x, k);
	push_up(rt);
}

Tr query(int rt, int l, int r, int x, int y) {
	if (x <= l && r <= y) {
		return tr[rt];
	}
	int mid = l + ((r-l)>>1);
	if (y <= mid) return query(rt<<1, l, mid, x, y);
	if (x  > mid) return query(rt<<1|1, mid+1, r, x, y);
	return query(rt<<1, l, mid, x, y) + query(rt<<1|1, mid+1, r, x, y);
}

int stk[MAX_N];
int tt = 0;

void solve(){
	tt = 0;
	char opt[5];
	int x;
	build(1, 1, N);
	for (int i = 1; i <= M; ++i) {
		sc("%s", opt);
		sc("%lld", &x);
		if (*opt == 'D') {
			stk[++tt] = x;
			update(1, 1, N, x, 0);
		} else if (*opt == 'Q') {
			int r = query(1, 1, N, 1, x).r;
			int l = query(1, 1, N, x, N).l;
			if (l == r) {
				puts("0");continue;
			}
			pr("%lld\n", l - r - 1);
		} else {
			if (tt) update(1, 1, N, stk[tt--], 1);
		}
	}
}

signed main()
{
	#ifndef ONLINE_JUDGE
	//FILE_IN
	FILE_OUT
	#endif
	int T = 1;//cin >> T;
	while (~sc("%lld%lld", &N, &M)) solve();

	return AC;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hexrt

客官,请不要给我小费!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值