Tunnel Warfare HDU - 1540 3种思路 线段树 || 树状数组+二分 || STL

一、内容

 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

二、思路

  • 第一种思路:利用线段树求解, 线段树维护一个最长连续子段和,然后查询的时候判断x点是在左边区间还是右边区间,如果在左边区间就判断左边区间的rmax(包含右断点的最大连续子段和)的长度是否超过了x那个点,如果超过了那么直接返回 左边区间.lmax + 右边区间.rmax。 否则继续查询左边区间。x在右边区间同理。
  • 第二种思路:利用树状数组 + 二分。 每次二分搜索左边和右边连续的最后一个1的位置。2个位置相减就是连续的子段和了。
  • 第三种思路:利用set自带的红黑树。 如第二种思路,我们只需要求出左边右边第一个0的位置(毁坏的村庄),那么中间的必然是好的村庄。

三、代码

第一种线段树:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5e4 + 5;
struct Node {
	int lmax, rmax, ans; //代表左边开始的最大连续和 ans是整段区间的最大连子段和 
} tr[N << 2];
int n, m, x, rec[N], last;
char s[2];
void pushup(int id, int l, int r) {
	Node &root = tr[id], &lt = tr[id << 1], &rt = tr[id << 1 | 1];
	int mid = (l + r) >> 1;
	root.lmax = lt.lmax + (lt.lmax == mid - l + 1 ? rt.lmax : 0);  
	root.rmax = rt.rmax + (rt.rmax == r - mid ? lt.rmax : 0);
	root.ans = max(max(lt.ans, rt.ans), lt.rmax + rt.lmax); 
}
void build(int id, int l, int r) {
	if (l == r) {
		tr[id].ans = tr[id].lmax = tr[id].rmax = 1;
		return;
	}	
	int mid = (l + r) >> 1;
	build(id << 1, l, mid);
	build(id << 1 | 1, mid + 1, r);
	pushup(id, l, r); 
}
void update(int id, int l, int r, int x, int d) {
	if (l == r) {
		tr[id].ans = tr[id].lmax = tr[id].rmax = d;
		return;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) update(id << 1, l, mid, x, d);
	else update(id << 1 | 1, mid + 1, r, x, d);
	pushup(id, l, r);
}
int query(int id, int l, int r, int x) {
	if (tr[id].ans == 0 || l == r) { //到达根节点了 
		return tr[id].ans;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) {
		if (mid - x + 1 <= tr[id << 1].rmax)  {
			//那么说明左边区间的连续长度超过了x这个点 那么右边区间的rmax就必须加上了
			return tr[id << 1].rmax + tr[id << 1 | 1].lmax; 
		} else return query(id << 1, l, mid, x); 
	} else {
		if (x - mid <= tr[id << 1 | 1].lmax) {
			return tr[id << 1].rmax + tr[id << 1 | 1].lmax;
		} else return query(id << 1 | 1, mid + 1, r, x); 
	} 
}
int main() {
	while (~scanf("%d%d", &n, &m)) {
		build(1, 1, n);
		last = 0;
		for (int i = 1; i <= m; i++) {
			scanf("%s", s);
			if (s[0] == 'D') {
				scanf("%d", &x); 
				rec[++last] = x;
				update(1, 1, n, x, 0);
			} else if (s[0] == 'Q') {
				scanf("%d", &x); 
				printf("%d\n", query(1, 1, n, x));
			} else {
				update(1, 1, n, rec[last--], 1);
			}
		}
	}
	return 0;
}

第二种树装数组 + 二分:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int N = 5e4 + 5;
int n, m, c[N], x, rec[N], last;//rec记录销毁的村庄 last记录最后一个
bool vis[N];
char s[2];
void update(int x, int d) {
	for (int i = x; i <= n; i += i & (-i)) c[i] += d;
}
int get(int x) {
	int ans = 0;
	for (int i = x; i > 0; i -= i & (-i)) ans += c[i];
	return ans;
}
//x是当前的位置
int searchR(int x, int l, int r) {
	int num = get(x);
	while (l < r) {
		int mid = (l + r + 1) >> 1;
		int t = get(mid) - num;
		if (t == (mid - x)) {
			l = mid;
		} else {
			r = mid - 1;
		}
	}
	return r;
}
int searchL(int x, int l, int r) {
	int num = get(x);
	while (l < r) {
		int mid = (l + r) >> 1;
		int t = num;
		if (mid != 1) t -= get(mid - 1);
		if (t == (x - mid + 1)) {
			r = mid;
		} else {
			l = mid + 1;
		}
	}
	return l;
}
int main() {
	while (~scanf("%d%d", &n, &m)) {
		memset(c, 0, sizeof(c));
		memset(vis, true, sizeof(vis));
		for (int i = 1; i <= n; i++) update(i, 1);
		last = 0;
		for (int i = 1; i <= m; i++) {
			scanf("%s", s);
			if (s[0] == 'D') {
				scanf("%d", &x);
				rec[++last] = x;
				if (vis[x]) update(x, -1), vis[x] = false;
			} else if (s[0] == 'Q') {
				scanf("%d", &x);
				int t = get(x);
				if (x != 1) t -= get(x - 1);
				if (t == 0)  {
					printf("0\n");
					continue;
				}
				int l = searchL(x, 1, x);
				int r = searchR(x, x, n);
				printf("%d\n", r - l + 1);
			} else {
				if (vis[rec[last]]) {
					last--;	
				} else {
					vis[rec[last]] = true; // true为村庄完好 
					update(rec[last--], 1);	
				} 
			}
		}
	}
	return 0;
}

第三种STL:

#include <cstdio>
#include <set>
#include <iostream>
using namespace std;
const int N = 4e4 + 5;
int n, m, x, rec[N], last;
char s[2];
int main() {
	while (~scanf("%d%d", &n, &m)) {
		last = 0;
		set<int> st;
		for (int i = 1; i <= m; i++) {
			scanf("%s", s);
			if (s[0] == 'D') {
				scanf("%d", &x);
				rec[++last] = x;
				st.insert(x);
			} else if (s[0] == 'Q') {
				scanf("%d", &x);
				//查找set中 大于x的第一个作为r  小于x的第一个作为l
				set<int>::iterator t = st.lower_bound(x); 
				int r = (t == st.end() ? n + 1: *t); //得到的是右边第一个为0的点
				int l = (t == st.begin() ? 0 : *(--t)); //得到左边第一个为0的点
				if (r == x) printf("0\n");  
				else printf("%d\n", r - l - 1);  //可以自己推一下 
			} else {
				st.erase(rec[last--]); //在集合中删除那个元素	
			}	
		}
	}
	return 0;
} 
发布了356 篇原创文章 · 获赞 286 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/qq_41280600/article/details/104100921