BZOJ2217:Lollipop

题意

给定一个由1和2组成的序列,多次询问是否存在一个区间满足区间和=x

分析

结论:假如存在一个子串和为x,那么一定存在一个前缀,和为x或x+1
证明:可以认为原串是由和为x的串在开头和结尾添加若干数得到,再后边添加数不会对产生和为x或x+1的前缀产生影响,所以只考虑在前边添加数,认为后边多余的数可以删掉
若原来是一个和为x或x+1的串,那么如果在前边添加的数和最后一个数相等,去掉最后一个数之后依然是一个和为x或x+1的串
考虑新添加的数和最后的数不同的情况
若原来和为x,在前边添加1,那么这就变成了和为x+1的串;再前边添加2,把结尾的1去掉,则变成了和为x+1的串
若原来和为x+1,在前边添加1,把后边的2删掉,就变成了和为x的串;在前边添加2,把结尾的1去掉之后,若新的结尾是1,把1去掉之后变成和为x+1的串,若新的结尾是2,把2去掉之后变为和为x的串。一定存在新的结尾,即原来串的长度不可能为1,因为串的和为x+1而最后一个数是1,并且x>0

找到和为x直接输出,找到x+1时如何找到x?
我们将1-i这一段向右移,如果找到a[l]=1||a[r]=1那么就能找到x,那么我们设置nxt[i]表示从i出发的连续2序列的长度,有以下两种情况

\(1.nxt[1]<nxt[i],l=nxt[1]+1+1,r=i+nxt[i]\)
这时候能够保证a[r]为2,而l为什么加2,因为加1的话就等于x+1,所以要再右移一位减去1,这里请思考一下
\(2.nxt[1]>=nxt[i],l=nxt[i]+1,r=nxt[i]+i,这里要保证r<=n\)
这时候a[l]为2,a[r]为1

trick

代码

#include <cstdio>
const int maxn = 1001000;
int n,m,q;
char s[maxn];
int mp[maxn<<1],l,r,nxt[maxn];

int main()
{
	scanf("%d %d",&n,&m);
	scanf("%s",s);
	int ret=0;
	for(int i=0;i<n;++i)
	{
		ret+=(s[i]=='W')?1:2;
		mp[ret]=i+1;
	}
	for(int i=n-1;i>=0;--i) nxt[i+1]=(s[i]=='W')?0:nxt[i+2]+1;
	while(m--)
	{
		scanf("%d",&q);
		if(mp[q]) puts("TAK");
		else if(mp[q+1])
		{
			int i=mp[q+1];
			if(nxt[1]<nxt[i]) puts("TAK");
			else if(nxt[1]>=nxt[i]&&i+nxt[i]<=n) puts("TAK");
			else puts("NIE");
		}
		else puts("NIE");
	} 
	return 0;
}
posted @ 2017-06-24 23:52  遗风忘语  阅读(183)  评论(0编辑  收藏  举报