关键要点:
将这个数字序列转换成连续的求和序列sum[],所以这个求和序列sum[]必定是单增的。将寻找和值为S的连续子序列转化成寻找sum[i-1]+S的元素,如果存在,就找到对应右端点下标j;如果不存在,就找到第一个使和值大于S的右端点。
#include<cstdio>
const int N=100010;
int nearS=100000010;
int sum[N];
//这里采用二分法
//upperbound函数返回在[L,R)内第一个大于x的位置
int upperbound(int left,int right,int x){
int mid;
while(left<right){
mid=(left+right)/2;
if(sum[mid]>x){
right=mid;
}else{
left=mid+1;
}
}
//注意这里返回的数是大于x的位置,所以真正的位置应该是这里返回的位置-1
return left;
}
int main(){
int n,S;
scanf("%d%d",&n,&S);
sum[0]= 0;
for(int i=1;i<=n;i++){
scanf("%d",&sum[i]);
sum[i]=sum[i]+sum[i-1];
}
for(int i=1;i<=n;i++){//枚举左端点
int j=upperbound(i,n+1,sum[i-1]+S);//这里返回的数据是大于x的位置
if(sum[j-1]-sum[i-1]==S){//查找成功
nearS=S;
break;
}else if(j<=n&&sum[j-1]-sum[i-1]<nearS){//求出存在大于S的最小解
nearS=sum[j]-sum[i-1];//更新当前nearS
}
}
for(int i=1;i<=n;i++){
int j=upperbound(i,n+1,sum[i-1]+nearS);
if(sum[j-1]-sum[i-1]==nearS){
printf("%d-%d\n",i,j-1);
}
}
}