线段树 区间合并 - 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,pos]区间上最靠右的被摧毁的村庄
lf
(left false) - 在[pos,n]区间上最靠左的被摧毁的村庄
rf
(right false)
首先实现寻找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\n");
}else{
int lf = find_left_false(temp,1);
int rf = find_right_false(temp,1);
printf("%d\n",(rf-1) - (lf+1) + 1);
}
}
}
}
return 0;
}