倍增总结

给定 \(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;
}
posted @ 2022-04-04 17:13  xqy2003  阅读(58)  评论(0)    收藏  举报