【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
TWTWT
5
1
7
Sample Output
1 3
2 2
NIE
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 ",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 ",a,b); else printf("%d %d ",a+1,b); } } } return 0; }//5 9 TWTWT 1 2 3 4 5 6 7 8 9