【BZOJ2217】[Poi2011]Lollipop 乱搞

【BZOJ2217】[Poi2011]Lollipop

Description

有一个长度为n的序列a1,a2,...,an。其中ai要么是1("W"),要么是2("T")。
现在有m个询问,每个询问是询问有没有一个连续的子序列,满足其和为q。

Input

第一行n,m (1<=n,m<=1000000)
第二行这个序列,起始编号为1,终止编号为n
下面每行一个询问q,询问有没有一个连续的子序列,满足其和为q (1<=q<=2000000)

Output

对于每个询问,输出一行,如果有,输出这个序列的起点和终点(如果有多个输出任意一个);如果没有,输出“NIE”。

Sample Input

5 3
TWTWT
5
1
7

Sample Output

1 3
2 2
NIE

题解:非常奇怪的题。如果存在一个子串的和为x,那么一定有一个前缀的和为x或x+1(显然),如果存在一个前缀为x就已经做完了,那么我们考虑如何由一个前缀x+1得到一个子串x。

我们用类似于双指针的过程,维护指针l和r不断向右平移,如果l或r中有一个是1,那么我们将这个1扔掉就从x+1得到了x,否则l和r都是2,那么我们将整个区间整体向右平移一格,直到出现1为止。换句话说,我们可以记录对于每个位置,它后面最长的连续的2有多少个,然后将l和r整体平移那么多格。如果平移到序列末端还不行,则输出无解。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=1000010;
int n,m,sum;
int v[maxn],st[maxn<<1],l[maxn];
char str[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+(gc^'0'),gc=getchar();
	return ret*f;
}
int main()
{
	n=rd(),m=rd();
	scanf("%s",str);
	int i,x,a,b;
	for(i=1;i<=n;i++)	v[i]=(str[i-1]=='T')+1,st[sum+=v[i]]=i;
	for(l[n+1]=1,i=n;i>=1;i--)	l[i]=(v[i]==1)?0:l[i+1]+1;
	for(i=1;i<=m;i++)
	{
		x=rd();
		if(st[x])	printf("%d %d\n",1,st[x]);
		else
		{
			if(!st[x+1])	puts("NIE");
			else
			{
				a=1+min(l[1],l[st[x+1]]),b=st[x+1]+a-1;
				if(b>n)	puts("NIE");
				else	if(v[b]==1)	printf("%d %d\n",a,b);
				else	printf("%d %d\n",a+1,b);
			}
		}
	}
	return 0;
}//5 9 TWTWT 1 2 3 4 5 6 7 8 9
posted @ 2017-12-03 09:47  CQzhangyu  阅读(349)  评论(0编辑  收藏  举报