[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; }
相关文章推荐
- Use Spring Insight Developer to Analyze Code, Install it with Tomcat, and Extend it with Plugins--转载
- Hadoop —— 初识
- Linux 用户的 3 个命令行小技巧
- Nginx安装配置详解
- vmware克隆Centos6.4虚拟机网卡无法启动问题
- linux下LNMP环境搭建
- linux下卸载mysql rpm安装方式和源码安装方式的两种方法
- Linux下取IP地址
- linux的proc
- 修改Java文件不用重启Tomcat服务
- Android基本架构
- 《Java项目开发全程实录》的实例08企业门户网站(Jsp+javabean)学习笔记
- 新手玩Docker
- Linux_1.3_Coredump故障分析
- Linux_1.2_GDB调试程序
- Linux下的.sh文件在windows下修改后无法执行
- Linux strace命令
- Shell脚本中引用、调用另一个脚本文件的2种方法
- Install Docker on Mac OS X(转)
- raspberry树莓派 Linux USB无线网卡 连接wpa2无线