倍增总结
给定 \(n\) 个数 , 保证每个数是非负数 , 给定数 \(k\) ,求满足前缀和小于等于 \(k\) , 要求前缀和的下标尽可能大.
预处理前缀和 , 倍增地跑 , 与求 \(lca\) 的思想一致.
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e5+5;
int n,T,a[N],logn[N];
int main()
{
scanf("%d%d",&n,&T);
logn[0] = -1;
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i] += a[i-1];
logn[i] = logn[i>>1] + 1;
}
int p = 0 , sum = 0 , r = logn[n];
for(int j=r;j>=0;--j) //注意到 0
{
if( p+(1<<j)<=n && sum + a[ p+(1<<j) ] - a[p] <= T) // 小于等于 , 尽可能逼近.
sum += a[ p+(1<<j) ] - a[p] , p += (1<<j);
}
cout<<p;
return 0;
}
如果是找等于 \(T\) 的最小前缀下标 , $ sum + a[ p+(1<<j) ] - a[p] <= T $ 改为小于 \(T\) , 最后 \(p+1\) 即可.
还有另外一种思路 , 我们可以按照一个 \(p\) 的距离跳 , 能跳则跳 , 然后令\(p\) 乘 \(2\) (贪心地让距离变大) , 不能跳的话则令 \(p\) 除\(2\) , 这两种方法异曲同工.
#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e5+5;
int n,T,a[N];
int main()
{
scanf("%d%d",&n,&T);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
a[i] += a[i-1];
}
int p=1,k = 0,sum = 0;
while(p)
{
if( k+p<=n && sum + a[k+p] - a[k] <= T)
{
sum += a[k+p] - a[k] ;
k += p , p *= 2;
}
else p /= 2;
}
cout<<k;
return 0;
}

浙公网安备 33010602011771号