Tunnel Warfare
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 65536/32768 K (Java/Others)
Problem Description
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
题目大意:
n个村庄,编号1~n,“D x”表示摧毁x村庄,“Q x”表示查询包括x的连续村庄个数,”R“表示回复最后一个被摧毁的村庄。
不止一组数据!!!
英语不好没看出来。。。
1.线段树+单点更新+区间合并
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 50010;
struct node
{
int l, r; //区间左右边界
int ll, rl, ml; //ll:区间最左连续大小 rl:最右 ml:区间最大连续
};
node tree[MAXN<<2];
int q[MAXN];
void build(int i, int l, int r)
{
tree[i].l = l;
tree[i].r = r;
tree[i].ll = tree[i].rl = tree[i].ml = r - l + 1;
if(l == r)
return;
int mid = l + r >> 1;
build(i<<1, l, mid);
build(i<<1|1, mid+1, r);
}
void update(int i, int t, int f)
{
if(tree[i].l == tree[i].r)
{
tree[i].ll = tree[i].rl = tree[i].ml = f;
return;
}
int mid = tree[i].l + tree[i].r >> 1;
if(t <= mid)
update(i<<1, t, f);
else
update(i<<1|1, t, f);
tree[i].ml = max(max(tree[i<<1].ml, tree[i<<1|1].ml), tree[i<<1].rl + tree[i<<1|1].ll);
tree[i].ll = tree[i<<1].ll;
tree[i].rl = tree[i<<1|1].rl;
if(tree[i<<1].ml == tree[i<<1].r - tree[i<<1].l + 1) //左子区间满时,更新ll
tree[i].ll += tree[i<<1|1].ll;
if(tree[i<<1|1].ml == tree[i<<1|1].r - tree[i<<1|1].l + 1) //右子区间满时,更新rl
tree[i].rl += tree[i<<1].rl;
}
int query(int i, int t)
{
if(tree[i].l == tree[i].r || tree[i].ml == 0 || tree[i].ml == tree[i].r - tree[i].l + 1)
return tree[i].ml;
//t恰好位于左右子区间之间连续段时,直接返回
if(t >= tree[i<<1].r - tree[i<<1].rl + 1 && t <= tree[i<<1|1].l + tree[i<<1|1].ll - 1)
return tree[i<<1].rl + tree[i<<1|1].ll;
int mid = tree[i].l + tree[i].r >> 1;
if(t <= mid)
return query(i<<1, t);
else
return query(i<<1|1, t);
}
int main(int argc, char const *argv[])
{
int n, m;
while(~scanf("%d%d", &n, &m))
{
getchar();
int t;
char ch;
int top = -1;
build(1, 1, n);
while(m--)
{
ch = getchar();
if(ch == 'D')
{
scanf("%d", &t);
q[++top] = t;
update(1, t, 0);
}
else if(ch == 'Q')
{
scanf("%d", &t);
printf("%d\n", query(1, t));
}
else
{
if(top != -1)
update(1, q[top--], 1);
}
getchar();
}
}
return 0;
}
2.做题时还没写过区间合并,作死手写了个二叉树摸拟,死活不对,求大神指点。
思路:
- 每次销毁,找到最小连续区间,标记断点并建立左右孩子。
- 重建时总是重建最后一个,所以可以找到断点所在区间,清断断点并删除左右孩子。
#include <cstdio>
#include <stack>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 50010;
int q[MAXN];
int top;
struct node
{
int l, r, d; //l:区间左边界 r:区间右边界 d:断点 0表示无断点
node *lc, *rc; //d左边区间 d右边区间
};
//rt区间增加断点t
void del(node* rt, int t)
{
if(!(rt->d)) //无断点时插入断点
{
rt->d = t;
node *p;
if(rt->l < t)
{
p = new node;
p->l = rt->l;
p->r = t - 1;
p->d = 0;
p->lc = p->rc = NULL;
rt->lc = p;
}
if(t < rt->r) //存在左孩子
{
p = new node;
p->l = t + 1;
p->r = rt->r;
p->d = 0;
p->lc = p->rc = NULL;
rt->rc = p;
}
return;
}
if(t == rt->d) //存在右孩子
{
top--;
return;
}
if(t < rt->d) //区间存在断点 分左右
del(rt->lc, t);
else if(t > rt->d)
del(rt->rc, t);
}
//查找包括t的连续区间
int query(node* rt, int t)
{
if(!(rt->d)) //连续区间返回r-l+1
return rt->r - rt->l + 1;
else
{
if(t < rt->d) //分左右
return query(rt->lc, t);
else if(t > rt->d)
return query(rt->rc, t);
else //恰好等于断点 返回0
return 0;
}
}
//恢复t
void rec(node* rt, int t)
{
if(rt->d == t) //找到t断点 置0并去掉左右孩子
{
rt->d = 0;
if(rt->lc)
{
free(rt->lc);
rt->lc = NULL;
}
if(rt->rc)
{
free(rt->rc);
rt->rc = NULL;
}
}
else if(t < rt->d)
rec(rt->lc, t);
else
rec(rt->rc, t);
}
int main()
{
int n, m;
node *head = new node;
while(~scanf("%d%d", &n, &m))
{
getchar();
head->l = 1;
head->r = n;
head->d = 0;
head->lc= head->rc= NULL;
int t;
char ch;
int top = -1;
while(m--)
{
ch = getchar();
if(ch == 'D')
{
scanf("%d", &t);
q[++top] = t;
del(head, t);
}
else if(ch == 'Q')
{
scanf("%d", &t);
printf("%d\n", query(head, t));
}
else
{
if(top != -1)
rec(head, q[top--]);
}
getchar();
}
}
return 0;
}