题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1540
题目大意:
有n座房子,起初都是完好的,有m次操作,可以销毁i号房屋,可以重建最后一个销毁的房屋,可以查询i号房屋和几个房屋直接或间接相连。
题目思路:
这个明显是考察线段树的打标记,我们可以这样把问题转换一下,我们把完好的房屋记作1,把摧毁的房屋记作0,那么查询i号和几个房屋相连,就是查询i号最长连续多少个1,这样问题就简化了。
接下来思考线段树需要什么标记,对于一次查询,我要确定和i号相连的1那么左边有右边也可能有,对于一个区间,也就是线段树上的一个节点,我只需要记录他靠近左边有多少个连续1标记为len,右边有多少个连续1标记为ren,该区间最长的连续1标记为men(max_len简称men)
?接下来是建树。正常建树,并且把len,ren,men都初始化为区间长度(因为还没毁)。
? 接下来是修改,如果是销毁,那么就把该叶子结点的len,ren,men改为0,反之改为1(此处需要一个栈记录销毁记录),肯定是需要递归到叶子结点的,然后把这个结点修改,这个好说,但是回溯的时候因为有三个标记len,ren,men。分别怎么修改呢
1 . len :父亲结点的len等于左孩子的len,但是如果左孩子全是1,那么还要加上右孩子的len(ren同理)
2. men:等于该节点的len; ren ;左儿子.ren+右儿子.len; 三者的最大值(画个图就可以看出啦)
?接下来是这个题的灵魂所在,查询,查询i点的时候,我要找的是他和多少个1相连,那么先思考递归终点,什么时候可以返回?
1.叶子节点,这个不用问。
2.整个区间都是1,因为再往下递归也是1。
3.整个区间都是0,再往下递归也全是0。
然后如果在左儿子就要递归查询左儿子多少个连续1,但是!!!如果这个点在左儿子中到左儿子的右边界都是1,那么还要加上右儿子的len,画个图更好理解,右儿子同理。
记得每次清空栈。。。
小二,上代码 :
#include<iostream>
#include<string.h>
#include<stdio.h>
#include<stack>
using namespace std;
const int MAXN=5e4+5;
struct Point
{
int l,r,len,ren,men;
}C[MAXN*4];
void build(int p,int l,int r)
{
C[p].l=l;C[p].r=r;
C[p].len=C[p].ren=C[p].men=r-l+1;
if(l==r){return ;}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
}
void change(int p,int x,int d)
{
if(C[p].l==C[p].r){
C[p].ren=C[p].len=C[p].men=d;
return ;
}
int mid=(C[p].l+C[p].r)/2;
if(x<=mid)change(2*p,x,d);
else change(2*p+1,x,d);
C[p].len=C[2*p].len;
if(C[2*p].len==C[2*p].r-C[2*p].l+1)C[p].len+=C[2*p+1].len;
C[p].ren=C[2*p+1].ren;
if(C[2*p+1].ren==C[2*p+1].r-C[2*p+1].l+1)C[p].ren+=C[2*p].ren;
C[p].men=max(C[p].len,max(C[p].ren,C[2*p].ren+C[2*p+1].len));
}
int ask(int p,int x)
{
if(C[p].l==C[p].r||C[p].men==C[p].r-C[p].l+1||C[p].men==0){
return C[p].men;
}
int mid=(C[p].l+C[p].r)/2;
if(x<=mid)
{
if(C[2*p].r-x+1<=C[2*p].ren){
return ask(2*p,x)+C[2*p+1].len;
}
else return ask(2*p,x);
}
else{
if(x-C[2*p+1].l+1<=C[2*p+1].len){
return ask(2*p+1,x)+C[2*p].ren;
}
else return ask(2*p+1,x);
}
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m)){
build(1,1,n);
stack<int>s;//
while(s.size())s.pop();
while(m--)
{
getchar();
char c;
scanf("%c",&c);
if(c=='D'){
int x;cin>>x;
s.push(x);
change(1,x,0);
}
if(c=='R'){
int x=s.top();
s.pop();
change(1,x,1);
}
if(c=='Q'){
int x;cin>>x;
printf("%d\n",ask(1,x));
}
}
}
}