莫队 学习笔记

一.莫队

1.普通莫队

一般使用条件:

  • 操作:都是区间查询(不资瓷修改)

  • \(O(1)\)\([l,r]\) 推出 \([l,r+1]\quad [l-1,r]\) 的答案

  • 复杂度:\(O(n\sqrt n)\) 要能跑过

SP3267-D-query

板子题

开桶记录记录当前区间内每个值出现的个数 转移时若 \(cnt[x]=0\quad ans\)++ 即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=2e6+5;
const int M=2e6+5;
const int inf=0x7fffffff;
const int mod=10086;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,len,a[N],cnt[M],bl[N],ans,l=1,r=0,res[N];
struct node{
    int l,r,id;
    friend bool operator<(node a,node b){
        return (bl[a.l]^bl[b.l])?bl[a.l]<bl[b.l]:(bl[a.l]&1)?a.r<b.r:a.r>b.r;
    }
}q[N];
inl void add(int x){
    ans+=(!cnt[x]);
    cnt[x]++;
}
inl void del(int x){
    cnt[x]--;
    ans-=(!cnt[x]);
}
inl void getans(node q){
    while(r<q.r)add(a[++r]);
    while(l>q.l)add(a[--l]);
    while(l<q.l)del(a[l++]);
    while(r>q.r)del(a[r--]);
    res[q.id]=ans;
}
signed main(){
    n=read();len=sqrt(n);
    for(int i=1;i<=n;i++){
        a[i]=read();
        bl[i]=(i-1)/len+1;
    }
    m=read();
    for(int i=1;i<=m;i++)
        q[i].l=read(),q[i].r=read(),q[i].id=i;
    sort(q+1,q+m+1);
    for(int i=1;i<=m;i++)getans(q[i]);
    for(int i=1;i<=m;i++)writel(res[i]);
    return 0;
}

CF617E-XOR and Favorite Number

考虑异或的性质:一个数被异或两次答案是 \(0\)

我们维护一个异或和数组 则 \(x_l\oplus x_{l+1}\oplus...\oplus x_r=sum_r\oplus sum_{l-1}\)

然后就是莫队板子了 在桶里查询 \(k\oplus x\)

\([l,r]\) 的答案是 \(sum_r\oplus sum_{l-1}\) 所以输入要把 \(l\ -1\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=1e5+5;
const int M=2e6+5;
const int inf=0x7fffffff;
const int mod=10086;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,k,len,a[N],cnt[M],bl[N],ans,l=1,r=0,res[N];
struct node{
    int l,r,id;
    friend bool operator<(node a,node b){
        return (bl[a.l]^bl[b.l])?bl[a.l]<bl[b.l]:(bl[a.l]&1)?a.r<b.r:a.r>b.r;
    }
}q[N];
inl void add(int x){
    ans+=cnt[k^x];
    cnt[x]++;
}
inl void del(int x){
    cnt[x]--;
    ans-=cnt[k^x];
}
inl void getans(node q){
    while(r<q.r)add(a[++r]);
    while(l>q.l)add(a[--l]);
    while(l<q.l)del(a[l++]);
    while(r>q.r)del(a[r--]);
    res[q.id]=ans;
}
signed main(){
    n=read();m=read();k=read();len=sqrt(n);
    for(int i=1;i<=n;i++){
        a[i]=read()^a[i-1];
        bl[i]=(i-1)/len+1;
    }
    for(int i=1;i<=m;i++)
        q[i].l=read()-1,q[i].r=read(),q[i].id=i;
    sort(q+1,q+m+1);
    for(int i=1;i<=m;i++)getans(q[i]);
    for(int i=1;i<=m;i++)writel(res[i]);
    return 0;
}

2.带修莫队

带修莫队,顾名思义,就是带修改的莫队(废话)

只需比普通莫队多维护第三维时间即可

分块大小为 \(n^{\tfrac{2}{3}}\) 有最优复杂度 \(O(n^{\tfrac{5}{3}})\)

P1903 [国家集训队] 数颜色 / 维护队列

板子题 直接放代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e6+5;
const int inf=0x7fffffff;
const int mod=10086;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,len,a[N],cnt[N],tot[N],bl[N],ans,l=1,r=0,t=0,res[N],cntp,cntq;
char op;
struct node{
    int l,r,t,id;
    friend bool operator<(node a,node b){
        return (bl[a.l]^bl[b.l])?bl[a.l]<bl[b.l]:(bl[a.r]^bl[b.r])?bl[a.r]<bl[b.r]:a.t<b.t;
    }
}q[N];
struct Node{
    int pos,col;
}p[N];
inl void add(int x){
    ans+=(!cnt[x]);
    cnt[x]++;
}
inl void del(int x){
    cnt[x]--;
    ans-=(!cnt[x]);
}
inl void upd(node q,Node &p){
    if(q.l<=p.pos&&p.pos<=q.r){
        del(a[p.pos]);
        add(p.col);
    }
    swap(a[p.pos],p.col);
}
inl void getans(node q){
    while(r<q.r)add(a[++r]);
    while(l>q.l)add(a[--l]);
    while(l<q.l)del(a[l++]);
    while(r>q.r)del(a[r--]);
    while(t<q.t)upd(q,p[++t]);
    while(t>q.t)upd(q,p[t--]);
    res[q.id]=ans;
}
signed main(){
    n=read();m=read();len=pow(n,0.666);
    for(int i=1;i<=n;i++){
        a[i]=read();
        bl[i]=(i-1)/len+1;
    }
    for(int i=1;i<=m;i++){
        scanf("%s",&op);
        if(op=='Q')
            q[++cntq]={read(),read(),cntp,cntq};
        else
            p[++cntp]={read(),read()};
    }
    sort(q+1,q+cntq+1);
    for(int i=1;i<=cntq;i++)getans(q[i]);
    for(int i=1;i<=cntq;i++)writel(res[i]);
    return 0;
}

CF940F-Machine Learning

统计每个数出现个数很简单 统计每种个数是否出现也很简单

那么考虑如何求出没出现过的个数的 \(mex\)

其实暴力求就可以了()

复杂度证明:设没出现过的个数的 \(mex=x\) 则一定会有出现 \(1\) 次、出现 \(2\)\(...\) 出现 \(x-1\) 次的数

则区间内数的数量 \(=\ \sum\limits_{i=1}^{x-1}i\ =\ \dfrac{x(x-1)}{2}\le n\)

因此单次求 \(mex\) 复杂度 \(O(\sqrt n)\)\(mex\) 总复杂度 \(O(n\sqrt n)\)

总复杂度还是 \(O(n^{\tfrac{5}{3}})\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e6+5;
const int inf=0x7fffffff;
const int mod=10086;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,len,a[N],cnt[N],tot[N],bl[N],ans,l=1,r=0,t=0,res[N],cntp,cntq,op,lsh[N],num;
struct node{
    int l,r,t,id;
    friend bool operator<(node a,node b){
        return (bl[a.l]^bl[b.l])?bl[a.l]<bl[b.l]:(bl[a.r]^bl[b.r])?bl[a.r]<bl[b.r]:a.t<b.t;
    }
}q[N];
struct Node{
    int pos,col;
}p[N];
inl void add(int x){
    tot[cnt[x]]--;
    tot[++cnt[x]]++;
}
inl void del(int x){
    tot[cnt[x]--]--;
    tot[cnt[x]]++;
}
inl void upd(node q,Node &p){
    if(q.l<=p.pos&&p.pos<=q.r){
        del(a[p.pos]);
        add(p.col);
    }
    swap(a[p.pos],p.col);
}
inl void getans(node q){
    while(r<q.r)add(a[++r]);
    while(l>q.l)add(a[--l]);
    while(l<q.l)del(a[l++]);
    while(r>q.r)del(a[r--]);
    while(t<q.t)upd(q,p[++t]);
    while(t>q.t)upd(q,p[t--]);
    for(ans=1;tot[ans];ans++);
    res[q.id]=ans;
}
signed main(){
    n=read();m=read();len=pow(n,0.666);
    for(int i=1;i<=n;i++){
        a[i]=lsh[++num]=read();
        bl[i]=(i-1)/len+1;
    }
    for(int i=1;i<=m;i++){
        op=read();
        if(op==1)
            q[++cntq]={read(),read(),cntp,cntq};
        else{
            p[++cntp]={read(),read()};lsh[++num]=p[cntp].col;
        }
    }
    sort(q+1,q+cntq+1);sort(lsh+1,lsh+num+1);
    int t=unique(lsh+1,lsh+num+1)-lsh-1;
    for(int i=1;i<=n;i++)
        a[i]=lower_bound(lsh+1,lsh+t+1,a[i])-lsh;
    for(int i=1;i<=cntp;i++)
        p[i].col=lower_bound(lsh+1,lsh+t+1,p[i].col)-lsh;
    for(int i=1;i<=cntq;i++)getans(q[i]);
    for(int i=1;i<=cntq;i++)writel(res[i]);
    return 0;
}

3.回滚莫队

莫队滚起来了

AT_joisc2014_c-歴史の研究

求区间内 \(a[i]*cnt[a[i]]\) 最大值

显然 区间扩大时答案很好维护 缩小时则无法 \(O(1)\) 修改

于是我们让莫队滚起来 这样只有增加操作了

\(a[i]\le 1e9\) 记得开 \(long\ long\) 和离散化

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e6+5;
const int inf=0x7fffffff;
const int mod=10086;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,len,a[N],cnt[N],tot[N],bl[N],ans,l=1,r=0,res[N],pos=1,lsh[N];
struct node{
    int l,r,id;
    friend bool operator<(node a,node b){
        return (bl[a.l]^bl[b.l])?bl[a.l]<bl[b.l]:a.r<b.r;
    }
}q[N];
inl void getans(int k){
    int l=min(k*len,n)+1,r=min(k*len,n),ans=0,pst;
    for(;pos<=m&&bl[q[pos].l]==k;pos++){
        node qus=q[pos];
        int lid=bl[qus.l],rid=bl[qus.r];
        if(lid==rid){
            for(int i=qus.l;i<=qus.r;i++){
                tot[a[i]]++;
                res[qus.id]=max(res[qus.id],tot[a[i]]*lsh[a[i]]);
            }
            for(int i=qus.l;i<=qus.r;i++)
                tot[a[i]]--;
            continue;
        }
        while(r<qus.r){
            cnt[a[++r]]++;
            ans=max(ans,cnt[a[r]]*lsh[a[r]]);
        }
        pst=ans;
        while(l>qus.l){
            cnt[a[--l]]++;
            ans=max(ans,cnt[a[l]]*lsh[a[l]]);
        }
        res[qus.id]=ans;
        ans=pst;
        while(l<min(k*len,n)+1)
            cnt[a[l++]]--;
    }
    while(r>min(k*len,n))
        cnt[a[r--]]--;
}
signed main(){
    n=read();m=read();len=sqrt(n);
    for(int i=1;i<=n;i++){
        a[i]=lsh[i]=read();
        bl[i]=(i-1)/len+1;
    }
    for(int i=1;i<=m;i++)
        q[i]={read(),read(),i};
    sort(q+1,q+m+1);sort(lsh+1,lsh+n+1);
    int t=unique(lsh+1,lsh+n+1)-lsh-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+t+1,a[i])-lsh;
    for(int i=1;i<=bl[n];i++)getans(i);
    for(int i=1;i<=m;i++)writel(res[i]);
    return 0;
}

P4137 Rmq Problem / mex

首先这题有个 \(\log\) 的主席树做法:维护每个值最后一次出现的下标,每次再 \(r\) 中查找最小的、最后一次出现的下标小于 \(l\) 的值

但是不想写主席树怎么办呢

显然每次缩小区间如果 \(cnt=0\) 能做到 \(O(1)\) 转移

于是我们就可以用回滚莫队碾掉这道题了

不要随便memset 很容易使复杂度假掉

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define inl inline 
#define int ll
#define gc getchar
#define pc putchar
const int N=1e6+5;
const int M=1e6+5;
const int inf=0x7fffffff;
const int mod=10086;
inl int read(){
    int x=0,f=1;char c=gc();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
    while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=gc();}
    return x*f;
}
inl void write(int x){
    if(x<0){pc('-');x=-x;}
    if(x>9)write(x/10);
    pc(x%10+'0');
}
inl void writel(int x){write(x);pc('\n');}
inl void writei(int x){write(x);pc(' ');}
inl void debug1(int x){pc('?');write(x);pc('\n');}
inl void debug2(int x){pc('#');write(x);pc('\n');}
int n,m,len,a[N],cnt[N],tot[N],bl[N],ans,l,r,res[N],pos=1,pans;
struct node{
    int l,r,id;
    friend bool operator<(node a,node b){
        return (bl[a.l]^bl[b.l])?bl[a.l]<bl[b.l]:a.r>b.r;
    }
}q[N];
inl void getans(int k){
    int pst;ans=pans;
    while(r<n)
        cnt[a[++r]]++;
    while(l<(k-1)*len+1){
        cnt[a[l++]]--;
        if(!cnt[a[l-1]])ans=min(ans,a[l-1]);
    }
    pans=ans;
    for(;pos<=m&&bl[q[pos].l]==k;pos++){
        node qus=q[pos];
        int lid=bl[qus.l],rid=bl[qus.r];
        if(lid==rid){
            for(int i=qus.l;i<=qus.r;i++)
                tot[a[i]]++;
            while(tot[res[qus.id]])res[qus.id]++;
            for(int i=qus.l;i<=qus.r;i++)
                tot[a[i]]--;
            continue;
        }
        while(r>qus.r){
            cnt[a[r--]]--;
            if(!cnt[a[r+1]])ans=min(ans,a[r+1]);
        }
        pst=ans;
        while(l<qus.l){
            cnt[a[l++]]--;
            if(!cnt[a[l-1]])ans=min(ans,a[l-1]);
        }
        res[qus.id]=ans;
        ans=pst;
        while(l>(k-1)*len+1)
            cnt[a[--l]]++;
    }
}
signed main(){
    n=read();m=read();len=sqrt(n);
    for(int i=1;i<=n;i++){
        a[i]=read();
        bl[i]=(i-1)/len+1;
    }
    for(int i=1;i<=m;i++)
        q[i]={read(),read(),i};
    sort(q+1,q+m+1);
    for(int i=1;i<=n;i++)cnt[a[i]]++;
    while(cnt[pans])pans++;
    l=1,r=n;
    for(int i=1;i<=bl[n];i++)getans(i);
    for(int i=1;i<=m;i++)writel(res[i]);
    return 0;
}
posted @ 2023-10-07 18:21  xiang_xiang  阅读(4)  评论(0编辑  收藏  举报