hdu 1540 Tunnel Warfare (线段树维护一维联通区间)

  • 题意: 一维线段上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;
}

当前节点与左右子节点区间的关系要搞清楚,只有单点修改,所以只存在用儿子更新父亲的情况.

上一篇:[NOI2005] 维护数列


下一篇:NOIP模拟测试8反思