首页 > 其他分享 >莫队

莫队

时间:2024-07-19 18:09:39浏览次数:6  
标签:int res 询问 答案 莫队 define

普通莫队

DQUERY - D-query

先想一下最朴素的暴力怎么写。显然可以用一个 \(cnt\) 数组记录每种元素的出现次数,然后如果这种元素是第一次出现,则增加答案,时间复杂度 \(O(nq)\)。

然后考虑如果如何用一个已经求出来答案的询问推出另外一个询问的答案。

显然我们要增加一部分数和删掉一部分数。对于删掉的数如果其出现次数为 \(2\),则答案减一;对于增加的数如果其出现次数为 \(0\),则答案加一。

然后莫队就是,在上面那种一个一个移动指针的情况下求答案。但是要预先知道所有询问,把所有询问离线下来,然后排序。

所以,莫队是一种离线算法

现在,我们将长度为 \(n\) 的序列分为 \(\sqrt n\) 个块,先按左端点所在块的编号从小到大排序,如果出现两个询问的这一项相同,则按右端点的位置从小到大排序,这样的时间复杂度是 \(O(n \sqrt n)\) 的。

事实上是非常好理解的,所以直接看一下代码:

#include<bits/stdc++.h>
#define int long long
#define N 50005
#define M 200005
#define S 1000005
using namespace std;
int n,m,len,w[N],ans[M],cnt[S];
struct node{
    int id,l,r;
}q[M];
int get(int x){
    return x/len;
}
bool cmp(const node &a,const node &b){
    int i=get(a.l),j=get(b.l);
    if(i!=j)return i<j;
    return a.r<b.r;
}
void add(int x,int &res){
    if(!cnt[x])res++;
    cnt[x]++;
}
void del(int x,int &res){
    cnt[x]--;
    if(!cnt[x])res--;
}
signed main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
    }
    cin>>m;
    len=max(1ll,(int)sqrt(n*n/m));
    for(int i=0;i<m;i++){
        int l,r;
        cin>>l>>r;
        q[i]={i,l,r};
    }
    sort(q,q+m,cmp);
    for(int i=0,j=1,k=0,res=0;k<m;k++){
        int id=q[k].id,l=q[k].l,r=q[k].r;
        while(i<r)add(w[++i],res);
        while(i>r)del(w[i--],res);
        while(j<l)del(w[j++],res);
        while(j>l)add(w[--j],res);
        ans[id]=res;
    }
    for(int i=0;i<m;i++){
        cout<<ans[i]<<'\n';
    }
    return 0;
}

代码是比较好理解的,不过注意四个移动指针的循环的顺序是有要求的,故不要随意更改顺序。

然后总结一下莫队的本质,就是在暴力的基础上把询问排个序,然后要求如果你知道询问 \([l,r]\) 的答案,在把 \(l\) 或 \(r\) 指针移动一格的情况下,仍然可以在 \(O(1)\) 复杂度内求出答案。

带修莫队

数颜色 / 维护队列

这里先说一下,带修莫队的块长一般使用 \(n^{\frac{2}{3}}\)。

带修莫队的差别和普通莫队事实上并不大,就是要加一个维度记录时间。

事实上,原来的四种转移方式再加上 \([l,r,t-1],[l,r,t+1]\) 两种转移方式就行了。

标签:int,res,询问,答案,莫队,define
From: https://www.cnblogs.com/zxh923aoao/p/18310097

相关文章

  • 【离线】- 莫队
    前言莫队是由莫涛提出的一种离线算法,是分块与双指针的结合,一般可以在\(O(n\sqrtn)\)或者\(O(n\sqrtm)\)的复杂度解决一些种类问题。普通莫队SP3267DQUERY-D-query给你一个长度为\(n\)的数列\(A\),\(m\)次询问,每次询问区间\([l,r]\)内的不同数字的个数。如果......
  • 带修莫队模板
    取分块大小\(n^\frac{2}{3}\)最优。例题:数颜色#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;constintN=1e6+5;intcnt[N],a[N];structquery{intl,r,t,id;//加入时间戳};structmodfiy{intpos,val;};intmain(......
  • 莫队模板
    #include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;constintN=1e6+5;intcnt[N],a[N];structquery{intl,r,id;};intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);intn;cin>>n;......
  • 从值域分块+莫队到二次离线莫队
    值域分块Q给定一个序列,实现单点修改\(O(1)\),以及区间查询\(O(\sqrtn)\)A考虑设\(block_i\)表示块\(i\)的和,那么修改便是\(O(1)\)全局查询时,整块调用\(block\),散块暴力即可\(O(\sqrtn)\)还有一些常见的例子,比如配合莫队代替主席树(区间mex)莫队二次离线普通莫队......
  • 算法课程笔记——普通莫队
    算法课程笔记——普通莫队......
  • 分块和莫队
    一、分块概念与作用1.概念:将数列等分为若干个不相交的区间,每一个区间称为一个块。2.作用:优化算法,降低复杂度。分块入门1题面:给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查询。分析:将n个元素等分成若干块,比如\(\{1,4,8,2,9,6,3,7,5\}\),等分成3块,则第一块包含的数......
  • 莫队
    膜拜“莫涛”巨佬思路如果说给你一个数组,有\(q\)组询问,询问一个区间的区间和,那么有最原始的做法。维护一个左端点和一个右端点,每次一位一位移动断点,那么时间复杂度是\(n\timesq\),那么如果我们将查询存起来,按一种我们想要的顺序去做呢?我们就可以排序,排序规则就是:B=sqrt(n......
  • 莫队(板子)
    莫队参考博客玄学暴力区间操作算法PPT解释的很清楚啦~,导致我没什么可写的\(qwq\)把所有询问离线下来后排序(左端点按块,右端点升序),然后从一个小区间通过左右端点的移动扩大区间,更新答案。复杂度主要在区间扩展,也就是左右指针的移动,对于莫队所有的优化几乎都是调整分块或排......
  • 莫队算法(基础莫队)小结(也做markdown测试)
    莫队基础莫队本质是通过排序优化了普通尺取法的时间复杂度。考虑如果某一列询问的右端点是递增的,那么我们更新答案的时候,右指针只会从左往右移动,那么i指针的移动次数是$O(n)$的。当然,我们不可能让左右端点都单调来做到总体$O(n)$。考虑对左端点进行分块。莫队排序:左端点按......
  • CF940F.Machine Learning-带修莫队、Mex
    给一个序列\(a\),两个操作:1、给\(l,r\),设\(a_l,\dots,a_r\)这些数集中每个数\(v\)的出现次数是\(c_v\),要求\(\mathrm{mex}(c_i)\).2、单点修改\(1\leqn,q\leq10^5\),时限4s这种一眼看过去很难维护的信息,一般就先找找性质。首先注意到关键性质:要求的是出现次数......