多语言展示
当前在线:1872今日阅读:97今日分享:45

莫队算法基础(c++写法)

莫队算法其实只是一种将暴力枚举优化的算法,主要是多次统计任意一段区间内数的和,只能用于离线,这里讲一个基础题给定一个大小为N的数组,数组中所有元素的大小<=N。你需要回答M个查询。每个查询的形式是L,R,K。你需要回答在范围[ L,R ]中至少重复K次的数字的个数。输入第一行一个整数N,M第二行有N个数第三行开始共M行,每行三个整数L,R,K输出共M行,每行一个整数。提示对于100%的数据满足:1≤m,n≤100000。原理:首先讲一下原理,它的原理其实是把全部的数分成(根号n)段,并将查找的区间按左节点所在的段排序,如果左节点在同一块中,那么就按右节点在所有数中的顺序排序,然后用两个指针左右移动统计,所以说和暴力枚举其实差别不大,不过挺省时间的,暴力枚举要用O(n^2),莫队算法只需要O(n*根号n)。
讲一下方法
1

输入并分段,然后排序,伪代码如下,先将输入进来的查询,按照左节点所在的段的先后和右节点的前后排序。bool cmp(const aa &a1,const aa &a2){    if(a1.t

2

这里来单独解释一下开的变量的用处struct aa{    int l,r,k,bian,t;//左节点,右节点,在范围[ L,R ]中至少重复K次的数字的个数,编号;//最后一个可有可无,这里只是为了方便分块排序}p[100001];int n,m,a[100001],q,ans[100001];//这些好理解吧,我就不解释了int cnt[100001],num[100001];//num[j]存j出现的次数,cnt[k]存出现不少于k次的数有几个

3

先预处理一遍第一个查询(排完序后)的cnt和num,具体代码如下 for(int i=p[1].l;i<=p[1].r;i++)num[a[i]]++,cnt[num[a[i]]]++;    ans[p[1].bian]=cnt[p[1].k];

4

然后从第二个到第m个开始搜索,开始不断更新cnt和num,记住,修改p[i-1].l或p[i+1].l、更改num和更改cnt的顺序你要自己理解,不能改顺序for(int i=2;i<=m;i++)    {        while(p[i-1].l>p[i].l){p[i-1].l--;num[a[p[i-1].l]]++,cnt[num[a[p[i-1].l]]]++;}//如果之前那个查询的左节点在当前查询的左节点的右边,将之前查询的左节点往左移知道与当前查询的左节点重合,并在移的过程中不断更新cnt和num值,下面三条语句也是这个用处。        while(p[i-1].lp[i].r){cnt[num[a[p[i-1].r]]]--,num[a[p[i-1].r]]--;p[i-1].r--;}        while(p[i-1].r

5

最后输出答案,这没什么好说的for(int i=1;i<=m;i++)printf('%d\n',ans[i]);

举个实际的例子
1

样例输入10 3 1 2 3 1 1 2 1 2 3 1 1 4 1 1 10 22 7 1 样例输出333

2

先将查询排序拍后的结果1 4 12 7 11 10 2

3

先预处理第一个

4

接着查找第二个,不断移动左右指针,更改cnt和num值

5

查找第三个,同第二个的方法

注意事项
1

记住,排序不是排数组中的数,而是查找的数

2

排序前用结构体,记得保存每个查询在输入中的位置,方便输出

推荐信息