洛谷P1972:HH的项链(莫队算法)

这题可以离线数组数组,在线主席树,也可以离线莫队。
虽然数据加强让莫队挂了,作为裸题还是可以拿来练练手

莫队算法是从已经求过答案的区间,通过移动指针来得到询问区间的答案,因为已经求过的区间可以不用重复求。如果按右区间排序,用双指针会发现右指针只会移一遍。
但左指针在最差的情况下会从一端移到另一端,复杂度依然是O(n*m)
引用分块的思维,按左端点值进行分块,在块内按右区间排序,在一个块内的所有询问右指针移动依然是O(n),而左指针移动降为O(m * sqrt(n)),再加上均摊的情况,整体复杂度大概在O(sqrt(n) * m)。

核心代码依然是通过移动指针得到询问区间的答案,套用到了分块的思维,使得复杂度降下来。

虽然套用了分块的算法,但写的时候并不用真的分块存储,可以直接在原序列上按分块来排序。

网上大把莫队算法的详解,这里就不讲了

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 5e5 + 10;
const int mx = 1e3 +10;
int cnt[maxn],a[maxm],ans[maxn];
int n,q;
struct ss{
	int id,l,r;
}qes[maxm + 10];
int block,num,res;
int curleft = 0,curright = 0;
bool cmp(ss a,ss b) {
	return (a.l / block == b.l / block) ? a.r < b.r : a.l < b.l;
}
void query(int l,int r) {
	while(curleft < l) {
		cnt[a[curleft]]--;
		if(cnt[a[curleft]] == 0) res--;
		curleft++;
	}
	while(curleft > l) {
		curleft--;
		cnt[a[curleft]]++;
		if(cnt[a[curleft]] == 1) res++;
	}
	
	while(curright < r) {
		curright++;
		cnt[a[curright]]++;
		if(cnt[a[curright]] == 1) res++;
	}
	while(curright > r) {
		cnt[a[curright]]--;
		if(cnt[a[curright]] == 0) res--;
		curright--;
	}
}
int main() {
	scanf("%d",&n);
	int mx = 0;
	for(int i = 1; i <= n; i++) {
		scanf("%d",&a[i]);
	}
	scanf("%d",&q);
	for(int i = 1; i <= q; i++) {
		scanf("%d%d",&qes[i].l,&qes[i].r);
		qes[i].id = i;
	}
	block = n >= 2333 ? 2333 : n;
	curleft = curright = 0;
	res = 0;
	sort(qes + 1,qes + q + 1,cmp);
	for(int i = 1; i <= q; i++) {
		int p = qes[i].id;
		query(qes[i].l,qes[i].r);
		ans[p] = res;
	}
	for(int i = 1; i <= q; i++) {
		printf("%d\n",ans[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值