线段树 区间合并

线段树 区间合并 - HDU - 1540 - Tunnel Warfare

个人解法

思路介绍

1. 建树

所需要的全局变量

int stk_ptr = 0; // 栈顶指针
int stk[N]; // 模拟栈
char cmd[5]; // 接受命令'D','R','Q'
int find_leaf[N]; // 根据村庄编号访问其代表的叶子结点的序号
pii get_lr[N<<2]; // 获得当前结点所代表的村庄区间[left,right]
bool tree[N<<2]; // 记录区间[left,right]的村庄是否全都未被摧毁

首先构建线段树

每一个结点的意义是[left,right]区间的村庄是否全未被摧毁

显然[left,right] = true 当且仅当 [left,mid] = true && [mid+1,right] = true

void build_tree(int left,int right,int root){
    tree[root] = true; // 标记这段区间的村庄未被摧毁
    get_lr[root].first = left; 
    get_lr[root].second = right; // 记录当前结点所维护的区间[left,right]
    
    if(left == right){ 
        find_leaf[left] = root; // 记录叶子结点
    }else{
        int mid = (left+right)>>1;
        build_tree(left,mid,root<<1);
        build_tree(mid+1,right,root<<1|1);
    }
}

2.摧毁操作

首先,每一个村庄p代表一个叶节点[p,p].

摧毁一个村庄,首先会更新对应的叶结点为false,然后还需要更新其父结点为false

void destroy(int root){
    while(tree[root]){ // 如果当前结点未true则需要更新,如果未false则可以终止(再往后全都是false)
        tree[root] = false;
        root>>=1; // 不用死循环的问题,因为tree[0] == false,会自动中跳出
    }
}

3.重建操作

重建一个村庄,首先更新叶结点; 如果兄弟结点也为true,则更新父结点为true

返回兄弟结点函数

int find_bro(int root){
    if(root & 1){
        return root ^ 1;
    }else{
        return root | 1;
    }
}

重建函数

void rebuild(int root){
    while(true){
        tree[root] = true;
        if(tree[find_bro(root)]){ // 如果兄弟为true才更新父结点为true
            root >>= 1;
        }else{
            break; // 不用死循环的问题,因为tree[0] == false,会自动中跳出
        }
    }
}

4.查询操作

当时在查询操作上卡了好久,一直想着如何判断左端点和右端点.后来发现,只需要找到两个点

  1. 在[1,pos]区间上最靠右的被摧毁的村庄lf (left false)
  2. 在[pos,n]区间上最靠左的被摧毁的村庄rf (right false)
image-20201030154009525

首先实现寻找lf的函数

int find_left_false(int pos,int root){ // 在[1,pos]区间中找到最靠右的false点
    if(tree[root]){
        return get_lr[root].first-1; // 如果整个区间都是true,那么返回区间左端点-1 
        
    }else if(get_lr[root].first == get_lr[root].second){ // 访问到了叶结点,则这棵树中最靠右的false点为区间左端点
        return get_lr[root].first;
        
    }else{
        int mid = (get_lr[root].first + get_lr[root].second) >> 1;
        if(pos <= mid){ // 舍弃右子树,因为右子树不在区间[1,pos]内
            return find_left_false(pos,root<<1); 
        }else{ // 左右都需要考虑,优先考虑右子树,因为右子树可能存在更靠右的false结点
            int a = find_left_false(pos,root<<1|1);
            if(a == get_lr[root<<1|1].first - 1){ // 右子树全为true
                a = find_left_false(pos,root<<1);
            }
            return a;
        }
    }
}

实现寻找rf的函数

int find_right_false(int pos,int root){
    if(tree[root]){
        return get_lr[root].second+1;
    }else if(get_lr[root].first == get_lr[root].second){
        return get_lr[root].first;
    }else{
        int mid = (get_lr[root].first + get_lr[root].second) >> 1;
        if(pos > mid){ // 舍弃左子树
            return find_right_false(pos,root<<1|1);
        }else{ // 左右都需要考虑,优先考虑左子树
            int a = find_right_false(pos,root<<1);
            if(a == get_lr[root<<1].second + 1){ // 左子树全为true
                a = find_right_false(pos,root<<1|1);
            }
            return a;
        }
    }
}

完整代码

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 50000+5
#define MIN(a,b) (a<b?a:b)
#define MAX(a,b) (a>b?a:b)
typedef pair<int,int> pii;


int stk_ptr = 0;
int stk[N];
char cmd[5];
int find_leaf[N]; // 根据位置访问其代表的叶子结点的序号
pii get_lr[N<<2]; // 获得当前结点所代表的区间[left,right]
bool tree[N<<2]; // 记录一段区间[left,right]是否都是互通的


void build_tree(int left,int right,int root){
    tree[root] = true; 
    get_lr[root].first = left;
    get_lr[root].second = right;
    if(left == right){
        find_leaf[left] = root;
    }else{
        int mid = (left+right)>>1;
        build_tree(left,mid,root<<1);
        build_tree(mid+1,right,root<<1|1);
    }
}

int find_bro(int root){
    if(root & 1){
        return root ^ 1;
    }else{
        return root | 1;
    }
}

void destroy(int root){
    while(tree[root]){
        tree[root] = false;
        root>>=1;
    }
}

void rebuild(int root){
    while(true){
        tree[root] = true;
        if(tree[find_bro(root)]){ 
            root >>= 1;
        }else{
            break; 
        }
    }
}

int find_left_false(int pos,int root){ // 在[1,pos]区间中找到最大的false点
    if(tree[root]){
        return get_lr[root].first-1;
    }else if(get_lr[root].first == get_lr[root].second){
        return get_lr[root].first;
    }else{
        int mid = (get_lr[root].first + get_lr[root].second) >> 1;
        if(pos <= mid){ // 舍弃右子树
            return find_left_false(pos,root<<1);
        }else{ // 左右都需要考虑,优先考虑右子树
            int a = find_left_false(pos,root<<1|1);
            if(a == get_lr[root<<1|1].first - 1){ // 右子树全为true
                a = find_left_false(pos,root<<1);
            }
            return a;
        }
    }
}

int find_right_false(int pos,int root){
    if(tree[root]){
        return get_lr[root].second+1;
    }else if(get_lr[root].first == get_lr[root].second){
        return get_lr[root].first;
    }else{
        int mid = (get_lr[root].first + get_lr[root].second) >> 1;
        if(pos > mid){ // 舍弃左子树
            return find_right_false(pos,root<<1|1);
        }else{ // 左右都需要考虑,优先考虑左子树
            int a = find_right_false(pos,root<<1);
            if(a == get_lr[root<<1].second + 1){ // 左子树全为true
                a = find_right_false(pos,root<<1|1);
            }
            return a;
        }
    }
}
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m) != EOF){
        stk_ptr = 0;
        int temp = 0;
        build_tree(1,n,1);
        while(m--){
            scanf("%s",cmd);
            if(cmd[0] == 'D'){
                scanf("%d",&temp);
                stk[stk_ptr++] = temp;
                destroy(find_leaf[temp]);
            }else if(cmd[0] == 'R'){
                temp = stk[--stk_ptr];
                rebuild(find_leaf[temp]);
            }else{
                scanf("%d",&temp);
                if(tree[find_leaf[temp]] == false){
                    printf("0
");
                }else{
                    int lf = find_left_false(temp,1);
                    int rf = find_right_false(temp,1);
                    printf("%d
",(rf-1) - (lf+1) + 1);
                }
            }
        }
    }
    return 0;
}
---- suffer now and live the rest of your life as a champion ----
原文地址:https://www.cnblogs.com/popodynasty/p/13902587.html