【洛谷P3514】LIZ-Lollipop【思维】【模拟】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P3514
给一个只有1和2的序列,每次询问有没有一个子串的和为xx


思路:

思维题。
如果序列[l,r][l,r]之和为kk,那么

  • a[l]=2a[l]=2时,i=l+1ra[i]=k2\sum^{r}_{i=l+1}a[i]=k-2
  • a[r]=2a[r]=2时,i=lr1a[i]=k2\sum^{r-1}_{i=l}a[i]=k-2
  • a[l]=a[r]=1a[l]=a[r]=1时,i=l+1r1a[i]=k2\sum^{r-1}_{i=l+1}a[i]=k-2

所以,如果有一段自区间[l,r][l,r]之和为kk,那么必然有一段区间的和为k2k-2
归纳得,如果一段区间之和为kk,那么必然有区间之和为k' (k与k'奇偶性相同)
所以分类来讨论,找到区间之和最大的奇数和偶数,然后比他们小的数字都可以得到。预处理出每一个数的区间即可。
这样离线的时间复杂度就是O(mlogn)O(m\log n)了。


代码:

#include <cstdio>
using namespace std;

const int N=1000010;
int n,m,L,R,sum,sum1,sum2,a[N],l[N*2],r[N*2];
char ch;

int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		while (ch=getchar()) if (ch=='W'||ch=='T') break;
		if (ch=='W') a[i]=1;
			else a[i]=2;
		sum+=a[i];  //求和
	}
	
	l[sum]=1; r[sum]=n;
	L=1; R=n;
	for (int i=sum-2;i>0;i-=2)
	{
		if (a[L]==2) L++;
		else if (a[R]==2) R--;
		else L++,R--;  //3种转移方式
		l[i]=L,r[i]=R;  //记录答案
	}
	
	sum1=sum2=sum;
	for (L=1;a[L]==2;L++) sum1-=2;
	for (R=n;a[R]==2;R--) sum2-=2;
	sum1--; sum2--; L++; R--;  //找到最大奇数(偶数)
	if (sum1>sum2)
	{
		l[sum1]=L,r[sum1]=n;
		R=n; sum=sum1;
	}
	else
	{
		l[sum2]=1,r[sum2]=R;
		L=1; sum=sum2;
	}
	for (int i=sum-2;i>0;i-=2)
	{
		if (a[L]==2) L++;
		else if (a[R]==2) R--;
		else L++,R--;
		l[i]=L,r[i]=R;
	}
	
	int x;
	while (m--)
	{
		scanf("%d",&x);
		if (!l[x]) printf("NIE\n");
			else printf("%d %d\n",l[x],r[x]);
	}
	return 0;
}
posted @ 2019-05-24 19:02  全OI最菜  阅读(106)  评论(0编辑  收藏  举报