洛谷 1503

      这道题我们抽象成基本模型,问题就是给你一个开始全是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;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值