您的位置:首页 > 其它

HDU 1540【线段树区间合并】

2018-01-26 18:01 274 查看
During the War of Resistance Against Japan, tunnel warfare was carried out extensively in the vast areas of north China Plain. Generally speaking, villages connected by tunnels lay in a line. Except the two at the ends, every village was directly connected with two neighboring ones.

Frequently the invaders launched attack on some of the villages and destroyed the parts of tunnels in them. The Eighth Route Army commanders requested the latest connection state of the tunnels and villages. If some villages are severely isolated, restoration of connection must be done immediately!

Input

The first line of the input contains two positive integers n and m (n, m ≤ 50,000) indicating the number of villages and events. Each of the next m lines describes an event.

There are three different events described in different format shown below:

D x: The x-th village was destroyed.

Q x: The Army commands requested the number of villages that x-th village was directly or indirectly connected with including itself.

R: The village destroyed last was rebuilt.

Output

Output the answer to each of the Army commanders’ request in order on a separate line.

Sample Input

7 9

D 3

D 6

D 5

Q 4

Q 5

R

Q 4

R

Q 4

Sample Output

1

0

2

4

题意:

1~n连续的区间,m个操作,D表示炸毁这个点,R表示修复上一个被炸毁的点,Q询问此点所在连续区间的长度

分析:

题目有几个坑点需要注意,注意多组输入,炸坏过的点可以重复修复(多次炸坏的点一次修复即可),修复时注意是否全部已经完好;

普通做法是用数组模拟(当然我看到有人用set+二分过了),记录端点值并查询从而相减作出区间,这个地方是需要优化的。用线段树去维护区间长度,这个长度可能不是连续的,但一定能通过左右子树进行相加合并,进而实现连续。

一开始我想用lazy标记,在query的时候再消点,可是貌似出现了一点问题,仔细想想加个lazy并没有进行优化,因为是单点更新。。。

*做这种题最好画图,用普通方法解,再把超时的部分用线段树维护,log级别的查找与更新;

*单点更新直接写就行了,区间更新加lazy,在用到的时候状态下推;

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
using namespace std;

typedef long long LL;
const int MAXN = 5e4 + 10;
int lsum[MAXN << 2], rsum[MAXN << 2];
bool vis[MAXN << 2];

void pushup(int root, int L, int R) {
int mid = (L + R) >> 1;
lsum[root] = lsum[root << 1]; //状态向上转移,利于查询
rsum[root] = rsum[root << 1 | 1];
if(lsum[root] == mid - L + 1) lsum[root] += lsum[root << 1 | 1]; //如果左子树叶子全部连续,把右子树的左端并到左子树
if(rsum[root] == R - mid) rsum[root] += rsum[root << 1]; //同上
}

void build(int root, int L, int R) {
lsum[root] = rsum[root] = R - L + 1; //初始全部连续,区间值最大
if(L == R) return ;
int mid = (L + R) >> 1;
build(root << 1, L, mid);
build(root << 1 | 1, mid + 1, R);
pushup(root, L, R);
}

void updata(int root, int L, int R, int x, bool flag) {
if(L == R) { //单点更新,向上统计
lsum[root] = rsum[root] = flag; //flag表示是否炸毁
return ;
}
int mid = (L + R) >> 1;
if(mid >= x) updata(root << 1, L, mid, x, flag);
else updata(root << 1 | 1, mid + 1, R, x, flag);
pushup(root, L, R);
}

int query(int root, int L, int R, int x) {
if(L == R) { //单点查询,但是复杂度会很低,因为能查到的点会很少,都被剪枝了
//  printf("%%%%%%%%%%%%%%\n");
return lsum[root];
}
int mid = (L + R) >> 1;
if(mid >= x) {
if(mid - x + 1 <= rsum[root << 1]) { //如果x点在左子树的右端范围内,返回左子树的右端值+右子树的左端值
return rsum[root << 1] + lsum[root << 1 | 1];
}
else { //否则继续寻找
return query(root << 1, L, mid, x);
}
}
else if(x > mid) { //同上
if(x - mid <= lsum[root << 1 | 1])
return lsum[root << 1 | 1] + rsum[root << 1];
else
return query(root << 1 | 1, mid + 1, R, x);
}
}

int main() {
int n, m, x;
while(~scanf("%d %d", &n, &m)) {
stack<int> s;
build(1, 1, n);
while(m--) {
getchar();
char ch;
scanf("%c", &ch);
if(ch == 'D') {
scanf("%d", &x);
s.push(x);
updata(1, 1, n, x, 0);
}
else if(ch == 'R') { //修复过的也可再修复,正常写就行,描述有点漏洞
if(s.empty()) continue; //判断是否有需要修复的了,坑点
int y = s.top();
s.pop();
updata(1, 1, n, y, 1);
}
else if(ch == 'Q') {
scanf("%d", &x);
printf("%d\n", query(1, 1, n, x));
}
}
}
return 0;
}


ef4d
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: