莫队算法基础和普通莫队

(描述:我是谁,我在哪?开玩笑,本来我现在应该写5级题的,但是最近有几个算法经常在我眼前晃悠,splay,莫队算法,还有昨天晚上zjx学长告诉我的悬线法,让我非常的好奇,另外就是5级题有些知识内容不会,没办法写,所以我现在也在补算法知识,顺便了解一下)

莫队算法:

1.莫队算法的核心代码(挪动的方式):

/*1.一般挪动方式,复杂度O(n*m)*/
inline void add(int x){//添加
	cnt[x]++;
	if(cnt[x] == 1) ans++;
}

inline void del(int x){//删除
	cnt[x]--;
	if(cnt[x] == 0) ans--;
}

// 在获取答案时
while(l > q[i].l) add(a[--l]);
while(r < q[i].r) add(a[++r]);
while(l < q[i].l) del(a[++l]);
while(r > q[i].r) del(a[--r]);

2.莫队算法的两种排序方式:

(1)第一种我看懂了,就是以右端点为第一关键字排序
代码:

//正常的排序方式
bool cmp(query a,query b){
	return (a.r/block)==(b.r/block) ? a.l < b.l : a.r < b.r;
}

(2)第二种说是奇偶性排序,但是我不太懂???

//奇偶性排序
bool cmp(node a,node b){
	return pos[a.l]^pos[b.l]?(pos[a.l]<pos[b.l]):(pos[a.l]&1?a.r < b.r:a.r > b.r);
}
/*这样能快的原因是因为指针移到右边后不用再跳回左边,
而跳回左边处理下一个块又要跳回右边,
这样能减少一半操作,理论上能快一倍
*/

3.莫队算法的分块方式:

块的大小是不固定的,但是要怎么分呢???不太懂

4.例题:

题意:求一个区间中每个数出现次数的平方和
还是设 cnt_i 为i在当前区间出现的次数
如果 cnt_i 多了一个,那
ans+=2 * cnt[x]+1
如果 cnt_i 多了一个,那
ans-=2 * cnt[x]-1
代码实现:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;

ll c[maxn];
ll sum[maxn];

struct node{
	ll l;
	ll r;
	ll num;
}q[maxn];

ll anss[maxn];
ll block;
ll ans = 0;

bool cmp(node a,node b){
	return (a.r / block) == (b.r / block) ? a.l < b.l : a.r < b.r;
}

inline void add(int x){//添加操作
	sum[c[x]]++;
	ans = ans + (2 * sum[c[x]] - 1);//想一想完全平方公式
}

inline void del(int x){//删除操作
	sum[c[x]]--;
	ans = ans - (2 * sum[c[x]] + 1);
}

int main(){
	ll n,m,k;
	scanf("%lld%lld%lld",&n,&m,&k);//输入
	
	block = sqrt(n);//分块
	
	for(ll i = 1;i <= n;i++){//输入
		scanf("%lld",&c[i]);
	}
	for(ll i = 1;i <= m;i++){
		scanf("%lld%lld",&q[i].l,&q[i].r);
		q[i].num = i;
	}
	sort(q + 1,q + 1 + m,cmp);//排序

	int l = 1; int r = 0;
	for(ll i = 1;i <= m;i++){//离线查询
		ll ql = q[i].l; ll qr = q[i].r;
		while(l < ql){
			del(l++);
		}
		while(r > qr){
			del(r--);
		}
		while(l > ql){
			add(--l);
		}
		while(r < qr){
			add(++r);
		}
		anss[q[i].num] = ans;
	}
	for(ll i = 1;i <= m;i++){//输出答案
		printf("%lld\n",anss[i]);
	}
	return 0;
}```

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值