- 题意: 一维线段上n个点,有摧毁和修复两种操作,询问某点最大的可达区域
- 思路: 线段树维护\(lsum\)代表区间左端点开始最长联通区域,\(rsum\)代表右端点开始最长联通区域.单点修改只有\(push_up\)操作,用左子树\(lsum\)更像\(lsum\),右子树\(rsum\)更新\(rsum\)
#include<string>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#define ll long long
#define FOR(i,l,r) for(int i = l ; i <= r ;++i )
#define inf 1<<30
#define EPS (1e-9)
#define lson(p) (p<<1)
#define rson(p) (p<<1|1)
using namespace std;
typedef pair<int,int> pii;
const int N = 5e5+30;
int n,m;
struct node{
int l,r;
int lsum,rsum;
}a[N<<2];
void push_up(int rt){
a[rt].lsum = a[lson(rt)].lsum; // 左二子更新左端点
a[rt].rsum = a[rson(rt)].rsum; // 右儿子更新右端点
if(a[lson(rt)].lsum + a[lson(rt)].l-1 == a[lson(rt)].r){
// 左儿子完全覆盖 扩充左区间
a[rt].lsum += a[rson(rt)].lsum;
}
if(a[rson(rt)].r - a[rson(rt)].rsum +1 == a[rson(rt)].l){
// 右儿子完全覆盖 扩充右区间
a[rt].rsum += a[lson(rt)].rsum;
}
}
void build(int rt,int l,int r){
a[rt].lsum = a[rt].rsum = r-l+1;
a[rt].l = l; a[rt].r = r;
if(l==r) return ;
int mid = (l+r)>>1;
build(lson(rt),l,mid);
build(rson(rt),mid+1,r);
}
void update(int rt,int p,int v){
// 单点更新
if(a[rt].l==p && a[rt].r ==p){
a[rt].lsum = v;
a[rt].rsum = v;
return ;
}
int mid = (a[rt].l + a[rt].r)>>1;
if(p<=mid) update(lson(rt),p,v);
else if(p>mid) update(rson(rt),p,v);
push_up(rt);
}
int query(int rt,int p){
// 单点
if(a[rt].l == a[rt].r )
return a[rt].lsum;
int mid = (a[rt].l + a[rt].r) >> 1;
if(p<=mid){
// p 在 左儿子的连续区间中, 左儿子的右区间与右儿子的左区间一定相邻 直接返回和
if(p >= a[lson(rt)].r - a[lson(rt)].rsum +1){
return a[lson(rt)].rsum + a[rson(rt)].lsum;
}else{ // 否则递归查询左儿子
return query(lson(rt),p);
}
}else{
if(p <= a[rson(rt)].lsum + a[rson(rt)].l-1){
return a[lson(rt)].rsum + a[rson(rt)].lsum;
}else{
return query(rson(rt),p);
}
}
}
char op[20];
void solve(){
stack<int> st;
build(1,1,n);
int pos ;
FOR(i,1,m){
scanf("%s",op);
if(op[0]=='D'){
scanf("%d",&pos);
st.push(pos);
update(1,pos,0);
}else if(op[0]=='Q'){
scanf("%d",&pos);
printf("%d\n",query(1,pos));
}else{
pos = st.top(); st.pop();
update(1,pos,1);
}
}
}
int main(){
while(scanf("%d%d",&n,&m)!=EOF){
solve();
}
return 0;
}
当前节点与左右子节点区间的关系要搞清楚,只有单点修改,所以只存在用儿子更新父亲的情况.