提供一种另类线段树做法。

为什么我总是想不出来正解,而是想出这些另类做法啊喂

由于样例连续修复了两次,这提示我们需要记录破坏的位置,并且后来的先被修复。那就用一个栈记录破坏位置。

线段树就维护两个值:

  • 当前区间最靠左的被摧毁的房子编号,以下称为 \(ld\) \(\texttt{(leftmost destroy)}\)
  • 当前区间最靠右的被摧毁的房子编号,以下称为 \(rd\) \(\texttt{(rightmost destroy)}\)

问:开局没有被破坏的时候,\(ld\)\(rd\) 应该设置为多少呢?

首先肯定不能简单地设为区间边界 \([l,r]\)\([l-1,r+1]\),因为那些地方可能在 \([1,n]\) 范围内,它们是有可能没有被破坏的。我们需要的是左边界无穷大,表示当前区间没有被破坏;同时右边界无穷小,同样表示当前区间没有被破坏。这里我取 \(0\) 表示无穷小,\(n+1\) 表示无穷大。

然后思考 \(\texttt{pushup}\)。有这样一种可能,当前区间的右区间有一个位置 \(pos\) 被破坏了,然后右区间的 \(ld\) 变成了 \(pos\),而左区间的 \(ld\) 还是 \(n+1\),这时就不能直接取左区间的 \(ld\) 了,而应该在左区间的 \(ld\) 和右区间的 \(ld\) 之间比个 \(\min\)

\(rd\) 的更新同理,应该在左区间的 \(rd\) 和右区间的 \(rd\) 之间比个 \(\max\)

接下来分析三个操作。

先来看破坏操作 D x。显然,这属于线段树的单点修改,先找到线段树包含目标点的叶节点(也就是长度为 \(1\) 的区间),然后把它的 \(ld\)\(rd\) 全部修改为当前区间的位置,最后 \(\texttt{pushup}\)。记得压栈。

再来看修复操作 R。这个就是把破坏操作反过来,不同的是把叶节点的 \(ld\) 改为 \(n+1\)\(rd\) 改为 \(0\)。 记得出栈。

最后是是查询 Q x

  • 如果士兵被困在左区间,说明右区间最左边的房子可能成为右边界,更新 \(ansr\),与当前右区间最靠左的被摧毁的房子编号比个 \(\min\),然后搜索右区间;

  • 如果士兵被困在右区间,说明左区间最右边的房子可能成为左边界,更新 \(ansl\),与当前左区间最靠右的被摧毁的房子编号比个 \(\max\),然后搜索右区间;

  • 如果区间长度为 \(1\) 就停止

还有一个小细节:

若士兵被围堵在摧毁了的房子中,那只能等死了。。。。。。

所以,需要开一个布尔数组记录当前这个点有没有被破坏,如果被破坏了直接输出 \(0\)

至此思路完结,上代码。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
#define div() cout << "-----------------------\n";
#define debug(n) cout << #n << " = " << n << "\n";
#define deb(n) cout << #n << " = " << n << "    ";
int n,m;int ansl,ansr;stack<int>s;bool des[50005];
struct Tree{
	int l,r,ld,rd;
}tree[200005];
// 建树
inline void build(int i,int l,int r){
	tree[i].l =  l;tree[i].r = r;
	tree[i].ld = n+1;tree[i].rd = 0;// 刚开始没有被破坏的房子
	if(l == r)return;
	int mid = (l+r) >> 1;
	build(2*i,l,mid);
	build(2*i+1,mid+1,r);
}
// 破坏
inline void modifyd(int i,int pos){
	if(tree[i].l == tree[i].r){
		tree[i].ld = tree[i].rd = pos;
		return;
	}
	int mid = (tree[i].l+tree[i].r) >> 1;
	if(pos <= mid){modifyd(2*i,pos);}
	else {modifyd(2*i+1,pos);}
	//	pushup
	tree[i].ld = min(tree[2*i].ld,tree[2*i+1].ld);
	tree[i].rd = max(tree[2*i].rd,tree[2*i+1].rd);
}
// 修复
inline void modifyr(int i,int pos){
	if(tree[i].l == tree[i].r){
		tree[i].ld = n+1;tree[i].rd = 0;
		return;
	}
	int mid = (tree[i].l+tree[i].r) >> 1;
	if(pos <= mid){modifyr(2*i,pos);}
	else {modifyr(2*i+1,pos);}
	//	pushup
	tree[i].ld = min(tree[2*i].ld,tree[2*i+1].ld);
	tree[i].rd = max(tree[2*i].rd,tree[2*i+1].rd);
}
// 查询
inline void query(int i,int pos){
	if(tree[i].l == tree[i].r){return;}
	int mid = (tree[i].l + tree[i].r) >> 1;
	if(pos <= mid){
		// 更新ansr
		ansr = min(ansr,tree[2*i+1].ld);
		query(2*i,pos);
	}
	else{
		//	更新ansl	
		ansl = max(ansl,tree[2*i].rd);
		query(2*i+1,pos);
	}
}
signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin >> n >> m;
    build(1,1,n);
    for(int i = 1;i <= m;i++){
		char c;cin >> c;
		if(c == 'D'){
			int x;cin >> x;
			s.push(x);des[x] = 1;// 入栈的同时将des[x]标记为1
			modifyd(1,x);
		}
		else if(c == 'R'){
			// 出栈的同时将des[x]标记为0
			int x = s.top();s.pop();des[x] = 0;
			modifyr(1,x);
		}
		else{
			int x;cin >> x;
			if(des[x]){cout << "0\n";continue;}
			ansl = 0;ansr = n+1;// 小心!ansr要初始化为无穷大
			query(1,x);
			// 答案是 (ansr-1)-(ansl+1)+1
			cout << (ansr-ansl-1) << '\n';
		}
	}
    return 0;
}