【HDU-1540】Tunnel Warfare

57 阅读2分钟
题意

有n个点,m个事件。

D x是破坏这个点;

Q x是询问包括x在内的最大连续区间是多少

R是恢复上一次破坏的点。

 

样例
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

 



 

AC代码
//因为村庄的序号是递增的,故可以使用二分查找
#include<bits/stdc++.h>
const int MAX=50001;
using namespace std;
int n,m;
int tree[MAX],cnt,pos,vis[MAX];   //vis数组的作用就是做标记,主要作用是解决Q查询动作,当vis数组代表哪个村庄已经被摧毁时,就不用查找了,直接为0
int lowbit(int x)
{
    return x&(-x);
}
void add(int x,int y)
{
    for(int i=x;i<=n;i+=lowbit(i))
        tree[i]+=y;
}
int getsum(int x)
{
    int sum=0;
    for(int i=x;i>0;i-=lowbit(i))
        sum+=tree[i];
    return sum;
}
//二分算法。注意:一个已经被摧毁的村庄可能会再次被摧毁!
int getmaxlen(int x)
{
    if(vis[x]!=1)    //如果这个村庄已经被摧毁,则肯定没有村庄与他相连了
        return 0;   
    int leftpos,rightpos;
    int left=1,right=x,mid;
    while(left<right)
    {
        mid=left+right>>1;
        if(getsum(x)-getsum(mid-1)<x-mid+1)
            left=mid+1;
        else right=mid;
    }
    leftpos=left;
    left=x,right=n+1;
    while(left<right)
    {
        mid=left+right>>1;
        if(getsum(mid)-getsum(x-1)<mid-x+1)
            right=mid;
        else left=mid+1;
    }
    rightpos=left;
    return rightpos-leftpos;

}
int main()
{
    //freopen("in.in","r",stdin);
    char op;
    while(cin>>n>>m)
    {
    	memset(tree,0,sizeof(tree));
    	//因为村庄会修复,并且是修复最近一次被摧毁的,这种形式使用栈会比使用数组更方便
    	stack<int>q;
    	///村庄正常时为1,被摧毁后为0
        for(int i=1;i<=n;i++)
        {
            add(i,1);//村庄未被摧毁时为1。以次建立每个村庄
            vis[i]=1;
        }
        cnt=pos=0;
        while(m--)
        {
            cin>>op;
            if(op=='D')
            {
                cin>>pos;
				q.push(pos);  //被炸毁,入栈
                if(vis[pos]==1)
                {
                	vis[pos]=0;  //标记数组复位
                	add(pos,-1);  //村庄被炸毁,由1变成0
                }
            }
            if(op=='R')
            {
            	if(!q.empty())
            	{
            		pos=q.top();
            		q.pop();      //找到刚刚被炸毁的村庄,可知这个村庄一定是在栈顶。由此可见使用栈的方便
            		if(vis[pos]==0)
            		{
            			vis[pos]=1;     //标记数组置位
						add(pos,1);   //村庄被修复
            		}
            	}
            }
            ///对查询
            //两次二分,分别把第i个数的左部分最长1串的长度和第i个数右部分最长1串的长度求出,
            //其中二分的判断方式是区间和是否为区间长度,区间和用树状数组维护。
            if(op=='Q')
            {
                cin>>pos;
                cout<<getmaxlen(pos)<<endl;
            }
        }
    }
}

 

题源:acm.hdu.edu.cn/showproblem…

参考:blog.csdn.net/hyc10/artic…