题目链接:https://hpuoj.com/contest/24/problem/E/
样例
input
3 5
2 3 5
output
4
开始的思路没错;前缀和表示,从第一个位置开始,到一个位置s,总和大于等于k,满足的区间为n-k+1,再从第二个位置开始,以此类推。。。。
开始的Time limit exceeded 代码:
for(int i=1; i<=n; i++)
{
int s;
for(int j=i;j<=n;j++)
sum[j]-=sum[i-1];//想着一个数用完以后就把这个数去掉,虽用二分,还是避免不了两层for循环
sum[i-1]=0;
s=lower_bound(sum,sum+n,k)-sum;//对二分位置变化这个地方还是错的
ans+=n-s+1;
//printf("%d\n",s);
}
正确的思路:从第一个数开始,到一个位置大于k,从第二个数开始,到一个位置大于k+sum[i-1],以此类推。。。
正确代码(二分):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k,ans;
long long a[1000006],sum[1000006];
int main(){
long long ans=0;
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]+a[i];
}
for(int i=1; i<=n; i++){
int s=lower_bound(sum+i,sum+n+1,sum[i-1]+k)-sum;
//printf("s---->%d\n",s);
ans+=(n-s+1);
//数组的下标是从0开始的,数组名表示数组的首地址,这里我们下标是从1开始的,所以sum+i,二分查找的是前闭后开,所以sum+n+1,遍历到最后一个数
}
printf("%lld\n",ans);
return 0;
}
也可以用尺取法来做:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,k;
long long a[1000006],ans;
int main(){
int l=1,sum=0;
scanf("%d%d",&n,&k);
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
for(int i=1; i<=n; i++){
sum+=a[i];
while(i<=n&&sum>=k){
ans+=n-i+1;
sum-=a[l++];
}
}
printf("%lld\n",ans);
return 0;
}