倍增算法总结 ( 含RMQ模板)

部分题目来自《算法竞赛设计进阶》

 

问题

      给定一个长度为n的数列A,有m个询问,每次给定一个整数T,求出最大的k,满足a[1],a[2]……a[k]的和小于等于T(不会打sigma)

       第一反应是二分,这个时候的复杂度是logn

       还有第二种解法,用倍增的思想,复杂度为logk(所求答案)。显然倍增要好很多。我讲讲倍增。

       如果是暴力的话显然是从前往后一个一个枚举来计算。这里每次只往后移了一格,我们能不能一下子移很多格呢?

       当然可以,我们可以用倍增的思想,第一次移1格,第二次移2格……如果再移就超过答案,就把移的格数除以2。如果最后移的格数是0,那么这一格就是答案

#include<cstdio>
#include<cctype>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 100 + 10;
int a[MAXN], s[MAXN], n, m, sum;

void read(int& x)
{
	int f = 1; x = 0; char ch = getchar();
	while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); } 
	while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); } 
	x *= f;
}

int main()
{
	read(n); read(m);
	_for(i, 1, n) read(a[i]), s[i] = s[i-1] + a[i];
	while(m--)
	{
		int T; read(T);
		int k = 0, p = 1, sum = 0; //k为当前在哪一格,p为下一步要移多少格,sum为已经走了的格的总和 
		while(p)
		{
			if(k + p <= n && sum + s[k + p] - s[k] <= T) 
			{
				sum += s[k + p] - s[k];
				k += p;
				p <<= 1;
			}
			else p >>= 1;
		}
		printf("%d\n", k);
	}
	return 0;
}

 

hihocoder#1384

发现倍增的套路和二分有点像。出题就出来判断区间是否合法上

这里值得一提的是可以把新加进入的数组sort一遍,然后和之前已经

有序的数组做一次归并,而不用从头再来sort

//这个程序WA,目前还不知道为什么
#include<cstdio>
#include<algorithm>
#include<cctype>
#include<cmath>
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
 
typedef long long ll;
const int MAXN = 5e5 + 10;
int n, m, a[MAXN];
int b[MAXN], t[MAXN];
ll T;

inline void read(int& x) 
{
	int f = 1; x = 0; char ch = getchar();
	while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
	while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
	x *= f;
}

inline void readll(ll& x) 
{
	ll f = 1; x = 0; char ch = getchar();
	while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); }
	while(isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
	x *= f;
}

void merge_sort(int L, int R, int p)
{
	_for(i, R + 1, R + p) b[i] = a[i];
	sort(b + R + 1, b + R + p + 1);
	
	int i = L, pos = L, j = R + 1;
	while(i <= R || j <= R + p)
	{
		if(j > R + p || i <= R && a[i] < b[j]) t[pos++] = a[i++];
		else t[pos++] = b[j++];
	}
	_for(i, L, R + p) a[i] = t[i];
}

bool check(int L, int R, int p)
{
	merge_sort(L, R, p);
	ll res = 0;
	int l = L, r = R + p, tmp = m;
	while(tmp--)
	{
		if(l >= r) break;
		res += (a[r] - a[l]) * (a[r] - a[l]);
		if(res > T) return false;
		r--; l++;
	}
	return true;
}
 
int main()
{
	int t; read(t);
	while(t--)
	{
		read(n); read(m); readll(T);
		_for(i, 1, n) read(a[i]);
	
		int L = 1, R, ans = 0, p;
		while(L <= n)
		{
			R = L; p = 1;
			ans++;
	   		while(p)
	   		{
	   	   		if(R + p <= n && check(L, R, p)) //
	   	   		{
	   	   	   		R += p;
			   		p <<= 1;
		   		}
		   		else p >>= 1;
	   		} 
	   		L = R + 1;
		}
		printf("%d\n", ans);
	}

	return 0;
}

 

RMQ算法模板 poj 3264(快速求区间最值,不支持修改)

#include<cstdio>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int MAXN = 8e4;
const int MAXM = 30;
int dmin[MAXN][MAXM], dmax[MAXN][MAXM], a[MAXN], n, q;

void RMQ_init()
{
	_for(i, 1, n) dmin[i][0] = dmax[i][0] = a[i];
	for(int j = 1; (1 << j) <= n; j++)
		for(int i = 1; i + (1 << j) - 1 <= n; i++)
		{
			dmax[i][j] = max(dmax[i][j-1], dmax[i + (1 << (j-1))][j-1]);
			dmin[i][j] = min(dmin[i][j-1], dmin[i + (1 << (j-1))][j-1]);
		}
}

int RMQ_ans(int l, int r)
{
	int k = 0;
	while((1 << (k + 1)) <= r - l + 1) k++;
	return max(dmax[l][k], dmax[r - (1 << k) + 1][k]) - min(dmin[l][k], dmin[r - (1 << k) + 1][k]);
}

int main()
{
	scanf("%d%d", &n, &q);
	_for(i, 1, n) scanf("%d", &a[i]);
	RMQ_init();
	while(q--)
	{
		int l, r;
		scanf("%d%d", &l, &r);
		printf("%d\n", RMQ_ans(l, r));
	}
	return 0;
}

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值