BZOJ2217 [Poi2011]Lollipop

结论:假如存在一个子串和为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的前缀直接输出,否则考虑如何把和为x+1的前缀变成和为x的子串

设当前这个前缀区间为[l,r],因为不存在和为x的前缀,所以最后一个数a[r]一定是1

我们把区间左右端点不断同时右移1个单位,当出现a[l]=1时,我们可以另x++得到和为x的子串,当a[r+1]=1时,我们可以另l++,r++得到和为x的子串

所以记录每个点之后最长连续2的长度,然后就可以搞了

若没有和为x或x+1的前缀,或者把r移到了n都没有找到合适的区间,那么就是无解

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 1000010
#define MAXM 1010
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
#define ll long long
int n,m;
char a[MAXN];
int s[MAXN],nxt[MAXN];
int v[MAXN<<1];
int main(){
	int i,x;
	scanf("%d%d%s",&n,&m,a+1);
	for(i=1;i<=n;i++){
		s[i]=s[i-1]+1+(a[i]=='T');
		v[s[i]]=i;
	}
	x=n+1;
	for(i=n;i;i--){
		if(a[i]=='W'){
			x=i;
		}
		nxt[i]=x-i;
	}
	while(m--){
		scanf("%d",&x);
		if(v[x]){
			printf("%d %d\n",1,v[x]);
		}else if(v[x+1]){
			int l=1,r=v[x+1];
			if(nxt[l]<nxt[r]){
				r+=nxt[l];
				l+=nxt[l]+1;
				printf("%d %d\n",l,r);
			}else if(r+nxt[r]<=n){
				l+=nxt[r];
				r+=nxt[r];
				printf("%d %d\n",l,r);
			}else{
				printf("NIE\n");
			}
		}else{
			printf("NIE\n");
		}
	}
	return 0;
}

/*

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值