ACM-区间查询之莫队算法

莫队算法(复杂度 n*sqrt(n))

听说是区间处理的神器,这个算法的本质是将区间分为0-sqrt(n), sqrt(n)-2sqrt(n),......,通过这个square root decompostion可以降低复杂度,使得区间查询的复杂度变为n*sqrt(n).

为什么这样分解有用呢?因为,我们发现,有些区间查询的结果可以利用上一次查询的结果来减少运算,这里有点DP的意味。以区间rsq为例(其实这个例子好像不太恰当,rsq问题用线段树的复杂度是 nlogn)。例子摘自geeksforgeeks。

算法步骤:

1. 首先对查询的区间排序,区间是[L,R],那么我们将L的 0-sqrt(n) , sqrt(n) - 2sqrt(n),分成不同的block,在同一个block中,我们将R从小到大排序。

2.第一次查询时,我们得到一个初始区间的rsq结果。

3.遍历之后的查询,定义变量 (lastL , lastR ,分别为上一次查询的[L,R] , curL,curR,为本次查询的[L,R] ,sum为上次查询的结果) 若curL< lastL,那么sum就要加上多出来的区间,若curL>lastL,sum就要减去多出来的区间。若curL==lastL,sum不变。后面也要比较curR和lastR,curR<lastR,那么sum就要减去多出来的区间,若curR>lastR,那么sum就要加上多出来的区间,curR==lastR,sum不变。

代码:

#include <bits/stdc++.h>
using namespace std;
int block;
bool cmp(pair<int, int> &fir, pair<int, int> &las) {
	if (fir.first / block != las.first / block)return fir.first / block<las.first / block;
	return fir.second<las.second;
}
void queryResults(int a[], int n, vector<pair<int, int> > q) {
	block = sqrt(n);
	sort(q.begin(), q.end(), cmp);
	int lastL = 0; int lastR = 0;
	int sum = a[0];
	for (int i = 0; i<(int)q.size(); i++){
		int CurL = q[i].first; int CurR = q[i].second;
		int tmpl = CurL; int tmpr = CurR;
		while (CurL<lastL) {
			sum += a[CurL];
			CurL++;
		}
		CurL = tmpl;
		while (CurL>lastL ) {
			CurL--;
			if(CurL >=0)
			sum -= a[CurL];
		}
		lastL = tmpl;
		while (CurR<lastR) {
			CurR += 1;
			sum -= a[CurR];
		}
		CurR = tmpr;
		while (CurR>lastR) {
			sum += a[CurR];
			CurR--;
		}
		lastR = tmpr;
		cout << "sum of " << "L [" << tmpl << " " << tmpr << " ] R " << sum << endl;
	}

}
int main() {
	int a[] = { 1, 1, 2, 1, 3, 4, 5, 2, 8 };
	int n = sizeof(a) / sizeof(a[0]);
	vector<pair<int, int>> q;
	q.push_back(make_pair(2, 2));
	q.push_back(make_pair(1, 4));
	q.push_back(make_pair(1, 3));
	q.push_back(make_pair(1, 5));
	q.push_back(make_pair(0, 5));
	q.push_back(make_pair(0, 4));
	q.push_back(make_pair(2, 4));
	queryResults(a, n, q); 
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值