【洛谷P3514】LIZ-Lollipop【思维】【模拟】
题目大意:
题目链接:https://www.luogu.org/problemnew/show/P3514
给一个只有1和2的序列,每次询问有没有一个子串的和为
思路:
思维题。
如果序列之和为,那么
- 当时,
- 当时,
- 当时,
所以,如果有一段自区间之和为,那么必然有一段区间的和为。
归纳得,如果一段区间之和为,那么必然有区间之和为k' (k与k'奇偶性相同)
所以分类来讨论,找到区间之和最大的奇数和偶数,然后比他们小的数字都可以得到。预处理出每一个数的区间即可。
这样离线的时间复杂度就是了。
代码:
#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;
}