1 #include <iostream> 2 #include <algorithm> 3 #include <cmath> 4 #include <stdio.h> 5 #include <cstring> 6 #include <string> 7 #include <cstdlib> 8 #include <queue> 9 #include <stack> 10 #include <set> 11 #include <vector> 12 #include <map> 13 #include <list> 14 #include <iomanip> 15 #include <fstream> 16 using namespace std; 17 typedef long long ll; 18 const int maxn=1000009; 19 ll a[maxn]; 20 int main() 21 { 22 //初始化前缀和数组a[maxn] 23 memset(a,0,sizeof(a)); 24 int cas; 25 scanf("%d",&cas); //读入测试样例组数 26 ll n,sum,ans,s,pos,l; 27 while(cas--) 28 { 29 ans=100005; sum=0; 30 scanf("%I64d%I64d",&n,&s); 31 for(int i=1;i<=n;++i) 32 { 33 scanf("%I64d",&a[i]); 34 sum+=a[i]; 35 a[i]+=a[i-1];//一边读一边计算前缀和 36 } 37 if(sum<s){//如果所有的数相加都没有要求的数字高 那么直接跳过这个样例 38 printf("0 "); 39 continue; 40 } 41 42 43 for(int i=1;i<=n;++i) 44 { 45 l=i-1;sum=0;pos=1;//倍增的思想,每次向右移动 pow(pos,2)的样子 46 while(pos)//当它移动到步长为0时 47 { 48 while(l+pos>n) pos>>=1;//因为是倍增,所以要担心它是否越界超出数组的实际长度,之前用if,还不够!!因为它减半一次以后仍然有可能超出范围必须用while 49 if(sum+a[l+pos]-a[l]<s)//之前的和sum加上之后区间的和如果仍然小于要求的数 50 { 51 sum+=a[l+pos]-a[l];//继续加上这段区间的和 52 l+=pos;//位置倍增,原来的右区间现在变成左区间 53 pos<<=1;//步长左移,相当于乘2 54 } 55 else{//发现sum加上之后区间的和大于等于要求的数,符合题目要求 56 ans=min(ans,l+pos-i+1);//那么答案就是原来答案与这整段区间的长度的较小值 57 pos>>=1;//因为步长太大了,所以pos步长需要右移一位,相当于除以2 58 } 59 } 60 //我一开始还在想,它假如pos除以2,回到上一步,上一步pos又乘2,不是在两者之间反复横跳吗...然后用草稿一步一步算以后发现 61 //它只是左端点不动,右边慢慢压缩pos 62 } 63 if(ans>100000)//发现答案长度超过数组实际长度,这不科学 64 printf("0 "); 65 else{ 66 printf("%d ",ans); 67 } 68 } 69 70 return 0; 71 }