提供一种另类线段树做法。
为什么我总是想不出来正解,而是想出这些另类做法啊喂
由于样例连续修复了两次,这提示我们需要记录破坏的位置,并且后来的先被修复。那就用一个栈记录破坏位置。
线段树就维护两个值:
- 当前区间最靠左的被摧毁的房子编号,以下称为 \(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;
}
浙公网安备 33010602011771号