这道题我们抽象成基本模型,问题就是给你一个开始全是1的序列,然后有2个操作,1是将一个位置的值 xor 1,2是询问x这个位置最左边的连续1的位置和最右边连续1的位置。我们可以怎么做呢?感觉一般的数据结构不太好弄,即使可以也要套一个二分什么的,那么我们可以考虑分块来做,我们将其分成根号n块,对于每一块,我们维护一个flag[i],flag[i]=1表示第i块全部是1,否则表示存在0,那么对于一次修改操作,我们只需要用根号n的时间维护flag[i],对于一次查询操作,我们先将x到x所在块的右区间扫一遍,如果发现0就返回,否则我们开始看x所在块的下一块的flag值,如果flag[i]=1,跳到下一块,否则暴力的扫找到1的位置,这样每次查询的复杂度也是根号n的,这样我们就能在m根号n的时间内解决,而且常数非常小。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 200005
const int block=224;
int a[maxn],L[450],R[450],b[maxn],cnt,n,m,last[maxn],tot;
bool flag[450];
int queryl(int x)
{
int temp=x-1;
for (int i=x-1;i>=L[b[x]];i--)
if (a[i]!=1) return temp-i;
x=L[b[x]]-1;
while (flag[b[x]]&&x>0) x=L[b[x]]-1;
if (x<=0) return temp;
for (int i=x;i>=L[b[x]];i--)
if (a[i]!=1) return temp-i;
}
int queryr(int x)
{
int temp=x;
for (int i=x;i<=R[b[x]];i++)
if (a[i]!=1) return i-temp;
x=R[b[x]]+1;
while (flag[b[x]]&&x<=n) x=R[b[x]]+1;
if (x>n) return n+1-temp;
for (int i=x;i<=R[b[x]];i++)
if (a[i]!=1) return i-temp;
}
void change(int x,int y)
{
a[x]=y;
bool ok=1;
for (int i=L[b[x]];i<=R[b[x]];i++)
{
if (a[i]!=1)
{
ok=0;
break;
}
}
if (ok) flag[b[x]]=1;
else flag[b[x]]=0;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++) a[i]=1;
for (int i=1;i<=n;i++)
{
b[i]=(i-1)/block+1;
if (b[i]!=b[i-1]) L[b[i]]=i,R[b[i-1]]=i-1;
}
L[1]=1;R[b[n]]=n;
for (int i=1;i<=b[n];i++)
{
bool ok=1;
for (int j=L[i];j<=R[i];j++)
if (a[j]!=1) {ok=0;break;}
if (ok) flag[i]=1;
}
scanf("%d",&m);
for (int i=1;i<=m;i++)
{
char opt[3];
int x;
scanf("%s",opt);
if (opt[0]=='R')
{
change(last[tot],1);
tot--;
continue;
}
scanf("%d",&x);
if (opt[0]=='D') {change(x,0);last[++tot]=x;}
if (opt[0]=='Q')
if (a[x]==0) printf("0\n");
else printf("%d\n",queryl(x)+queryr(x));
}
return 0;
}