1. 程式人生 > >【BZOJ2217】[Poi2011]Lollipop 亂搞

【BZOJ2217】[Poi2011]Lollipop 亂搞

getch 記錄 spa style using 子序列 ++ brush highlight

【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

【BZOJ2217】[Poi2011]Lollipop 亂搞