您的位置:首页 > 运维架构

[POI 2011]Lollipop(二分+特判)

2015-06-16 08:32 337 查看

题目链接

http://main.edu.pl/en/archive/oi/18/liz

题目大意

给你一个长度为nn的序列a[]a[],序列里只包含数字1或2,qq次询问一个数字xx,问序列里是否存在一个连续的区间[L,R][L,R],使得∑Ri=La[i]=x\sum_{i=L}^{R}a[i]=x,并输出一个可行的区间。

n,q≤106n,q\leq10^6

思路

网上题解比较少,做法都是一样的:即大小为xx的区间可行时,x−2x-2大小的区间也是可行的,因此可以离线回答所有询问,分奇数和偶数的xx分开讨论,先找出最大的奇数和偶数的xx以及一个可行区间,然后倒着推x−2,x−4...x-2,x-4...的可行区间

这里我提出一种新的做法(感谢温州中学au大爷林一诺提供的思路)。

我们对序列里包含的1的个数不超过1和大于1分开讨论:

1、1的个数小于等于1

在这个情况下,我们用特判来回答每个询问,总的复杂度O(q)O(q)

如果1的个数为0,那么询问的xx必须≤∑a[i]\leq \sum a[i],且2|x2|x

如果1的个数为1,若xmod2=∑a[i]mod2x \mod 2= \sum a[i] \mod 2,则询问的xx必须≤∑a[i]\leq \sum a[i];否则说明询问的xx是个偶数,必须只由2组成,看1左边的2之和和1右边的2之和分别是否大于等于xx(保证xx能由1左边的2或者1右边的2里找出一个子区间凑出)

2、1的个数大于1

我们找出这个序列里离左端点最近的1的位置LoneL_{one}和离右端点最近的1的位置n−Rone+1n-R_{one}+1

序列一定是这个样子:

22…221??…??122…22

若x>∑a[i]−2(min{Lone,Rone}−1)x>\sum a[i]-2(\min\{L_{one},R_{one}\}-1),则表明选择的区间必须不得不选择LoneL_{one}和n−Rone+1n-R_{one}+1两边的2,若xmod2=∑a[i]mod2x \mod 2= \sum a[i] \mod 2,且x≤sumx\leq sum,则显然找得到可行区间

其他情况下,我们可以考虑在[Lone+1,n][L_{one}+1,n]区间进行二分,以及在[1,n−Rone][1,n-R_{one}]区间进行二分找出一个位置tt,使得x−1≤∑ti=Lone+1≤xx-1\leq \sum _{i=L_{one}+1}^t\leq x,或者x−1≤∑n−Ronei=t≤xx-1\leq \sum _{i=t}^{n-R_{one}}\leq x,则找得到可行区间

显然这样的二分做法是正确的,因为只要存在合法区间,就肯定可以通过二分,在最左边或最右边的1旁边找出连续的一段和,恰好等于xx或x−1x-1,如果是x−1x-1的话,就把那个1补上去就成了xx(序列里只包含1或者2,因此只要存在合法区间,二分后就得不到一个x−3,x−4...x-3,x-4...大小的区间)

PS:若∑ti=Lone+1=x\sum _{i=L_{one}+1}^t= x,或者∑n−Ronei=t=x\sum _{i=t}^{n-R_{one}}= x,则答案区间就是[Lone+1,t][L_{one}+1,t]([t,n−Rone][t,n-R_{one}]);若∑ti=Lone+1=x−1\sum _{i=L_{one}+1}^t= x-1,或者∑n−Ronei=t=x−1\sum _{i=t}^{n-R_{one}}= x-1,则答案区间就是[Lone,t][L_{one},t]([t,n−Rone+1][t,n-R_{one}+1])

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 2100000

using namespace std;

int n,m,a[MAXN],cnt[3],sum=0; //cnt[1]=1的个数,cnt[2]=2的个数
char s[MAXN];

namespace SpecialCheck //只有一个1,特判
{
void solve()
{
int pos=-1; //pos=唯一的那个1的位置
for(int i=1;i<=n;i++)
if(a[i]==1)
{
pos=i;
break;
}
for(int i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
if(x>sum)
{
printf("NIE\n");
continue;
}
if((sum%2)==0&&(x%2)!=(sum%2))
{
printf("NIE\n");
continue;
}
if((sum%2)==1&&(x%2)==0)
{
if(2*(pos-1)<x&&2*(n-pos)<x)
{
printf("NIE\n");
continue;
}
}
if((sum%2)==0)
{
printf("1 %d\n",x/2);
continue;
}
//sum%2==1
if(x%2==0)
{
if(x/2<pos)
{
printf("1 %d\n",x/2);
continue;
}
printf("%d %d\n",n-x/2+1,n);
}
else
{
int need=x/2; //需要数字2的个数
int L=max(1,pos-need); //选取的区间左端点
int R=L+need;
printf("%d %d\n",L,R);
}
}
}
}

namespace BinarySearchEdition //二分版
{
int presum[MAXN],sufsum[MAXN]; //前缀和、后缀和
void solve()
{
int Lone,Rone; //最左边和最右边的1的位置
for(int i=1;i<=n;i++)
if(a[i]==1)
{
Lone=i;
break;
}
for(int i=n;i>=1;i--)
if(a[i]==1)
{
Rone=n-i+1;
break;
}
for(int i=1;i<=n;i++) presum[i]=presum[i-1]+a[i];
for(int T=1;T<=m;T++)
{
int x;
scanf("%d",&x);
if(x==1)
{
printf("%d %d\n",Lone,Lone);
continue;
}
if(x>sum-2*(min(Lone,Rone)-1))
{
if((x%2)!=(sum%2))
printf("NIE\n");
else if(x>sum)
printf("NIE\n");
else
{
int need=(x-(sum-2*(Lone+Rone-2)))/2; //需要的2的个数
int L=max(1,Lone-need);
int R=n-Rone+1+(need-(Lone-L));
printf("%d %d\n",L,R);
}
continue;
}

int lowerBound=Lone+1,upperBound=n,ans=-1;
while(lowerBound<=upperBound)
{
int mid=(lowerBound+upperBound)>>1;
if(presum[mid]-presum[Lone]<=x)
{
ans=mid;
lowerBound=mid+1;
}
else upperBound=mid-1;
}
if(ans!=-1)
{
if(presum[ans]-presum[Lone]==x)
{
printf("%d %d\n",Lone+1,ans);
continue;
}
else if(presum[ans]-presum[Lone]==x-1)
{
printf("%d %d\n",Lone,ans);
continue;
}
}
lowerBound=1,upperBound=n-Rone,ans=-1;
while(lowerBound<=upperBound)
{
int mid=(lowerBound+upperBound)>>1;
if(presum[n-Rone]-presum[mid-1]<=x)
{
ans=mid;
upperBound=mid-1;
}
else lowerBound=mid+1;
}
if(ans!=-1)
{
if(presum[n-Rone]-presum[ans-1]==x)
{
printf("%d %d\n",ans,n-Rone);
continue;
}
else if(presum[n-Rone]-presum[ans-1]==x-1) //!!!!!
{
printf("%d %d\n",ans,n-Rone+1);
continue;
}
}
printf("NIE\n");
}
}
}

int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s+1);
for(int i=1;i<=n;i++) a[i]=s[i]=='T'?2:1,cnt[a[i]]++,sum+=a[i];
if(cnt[1]<=1)
{
using namespace SpecialCheck;
solve();
}
else
{
using namespace BinarySearchEdition;
solve();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: