题意
有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…