学习笔记 莫队

学习笔记 莫队

讲解

某大佬的博客
里面讲的很详细

解决问题

主要解决序列上的问题
比如区间的不同元素个数等等乱七八糟的

主要方法

通过离线把区间分块排序来让暴力效率更高
哪怕是待修也可以通过增加一维解决

排序比较函数

一般的:

inline bool cmp(node a,node b) {
    return bl[a.l]==bl[b.l] ? a.r < b.r : bl[a.l] < bl[b.l];
}

有一定优化的:
先按照左端点的块排序假如块是奇数的时候右端点递增,否则递减

inline bool cmp1(node x,node y){
    return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : (bl[x.l]&1) ? x.r<y.r : x.r>y.r;
}

待修的:
增加了一维

inline bool cmp1(node x,node y){
    return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : ((bl[x.r]^bl[y.r]) ? bl[x.r]<bl[y.r] : x.ti < y.ti);
}

例题

SP3267 DQUERY - D-query

很正常的莫队,直接就是板子一样的
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=30010,maxm=1e6+10;
int m,n,a[maxm],bl[maxm],ans[maxm],cnt[maxm];
struct node{
    int l,r,id;
}q[maxm];
inline bool cmp1(node x,node y){
    return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : (bl[x.l]&1) ? x.r<y.r : x.r>y.r;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int sz=sqrt(n);
    int bln=ceil((double)n/sz);
    for(int i=1;i<=bln;i++)
        for(int j=(i-1)*sz+1;j<=min(n,i*sz);j++) bl[j]=i;
    scanf("%d",&m);
    for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+1+m,cmp1);
    int l=1,r=0,now=0;
    for(int i=1;i<=m;i++){
        int ql=q[i].l,qr=q[i].r;
        while(l<ql) now-=!--cnt[a[l++]];
        while(l>ql) now+=!cnt[a[--l]]++;
        while(r<qr) now+=!cnt[a[++r]]++;
        while(r>qr) now-=!--cnt[a[r--]];
        ans[q[i].id]=now;
    }
    for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}

Luogu_P2709 小B的询问

需要推一下加减的式子
代码如下:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=50010;
int n,m,k,a[maxn],bl[maxn],cnt[maxn];
ll now=0,ans[maxn];
struct node{
    int l,r,id;
}e[maxn];
inline bool cmp1(node x,node y){
    return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : (bl[x.l]&1) ? x.r<y.r : x.r>y.r;
}
inline void add(int x){
    cnt[a[x]]++;
    now+=(ll)2*cnt[a[x]]-1;
} 
inline void del(int x){
    cnt[a[x]]--;
    now-=(ll)2*cnt[a[x]]+1;
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=m;i++) scanf("%d%d",&e[i].l,&e[i].r),e[i].id=i;
    int sz=sqrt(n);
    int bln=ceil((double)n/sz);
    for(int i=1;i<=bln;i++)
        for(int j=sz*(i-1)+1;j<=min(n,sz*i);j++) bl[j]=i;
    sort(e+1,e+1+m,cmp1);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        int ql=e[i].l,qr=e[i].r;
        while(l<ql) del(l++);
        while(l>ql) add(--l);
        while(r<qr) add(++r);
        while(r>qr) del(r--);
        ans[e[i].id]=now;
    }
    for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    return 0;
}

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

带修的,增加一维。
要是不把add和del加入主函数会T,挺卡常然而我O2

#include<bits/stdc++.h>
using namespace std;
const int maxn=200003,maxcnt=1e6+10;
int n,m,a[maxn],bl[maxn],qcnt,ccnt,now=0,ans[maxn],cnt[maxcnt];
struct node{
    int l,r,ti,id;
}q[maxn];
struct edge{
    int wh,cl;
}c[maxn];
inline bool cmp1(node x,node y){
    return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : ((bl[x.r]^bl[y.r]) ? bl[x.r]<bl[y.r] : x.ti < y.ti);
}
inline void add(int x){
    if(!cnt[a[x]]) now++;
    cnt[a[x]]++;
}
inline void del(int x){
    cnt[a[x]]--;
    if(!cnt[a[x]]) now--;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int sz=pow(n,(double)2.0/3.0);
    int bln=ceil((double)n/sz);
    for(int i=1;i<=bln;i++)
        for(int j=sz*(i-1)+1;j<=min(n,sz*i);j++) bl[j]=i;
    for(int i=1;i<=m;i++){
        char s[10];scanf("%s",s);
        if(s[0]=='Q'){
            ++qcnt;scanf("%d%d",&q[qcnt].l,&q[qcnt].r);
            q[qcnt].ti=ccnt;q[qcnt].id=qcnt;
        }else{
            ++ccnt;scanf("%d%d",&c[ccnt].wh,&c[ccnt].cl);
        }
    }
    sort(q+1,q+1+qcnt,cmp1);
    int l=1,r=0,tme=0;now=0;
    for(int i=1;i<=qcnt;i++){
        int ql=q[i].l,qr=q[i].r,qt=q[i].ti;
        while(l<ql) del(l++);
        while(l>ql) add(--l);
        while(r<qr) add(++r);
        while(r>qr) del(r--);
        while(tme<qt){
            ++tme;
            if(ql<=c[tme].wh && c[tme].wh<=qr) {
                cnt[a[c[tme].wh]]--;
                if(!cnt[a[c[tme].wh]]) now--;
                if(!cnt[c[tme].cl]) now++;
                cnt[c[tme].cl]++;
            }
            swap(a[c[tme].wh],c[tme].cl);
        }
        while(tme>qt){
            if(ql<=c[tme].wh && c[tme].wh<=qr) {
                cnt[a[c[tme].wh]]--;
                if(!cnt[a[c[tme].wh]]) now--;
                if(!cnt[c[tme].cl]) now++;
                cnt[c[tme].cl]++;
            }
            swap(a[c[tme].wh],c[tme].cl);
            --tme;
        }
        ans[q[i].id]=now;
    }
    for(int i=1;i<=qcnt;i++) printf("%d\n",ans[i]);
    return 0;
}

易错点(我的)

cnt数组开不够空间
now的加减

posted @ 2019-10-16 15:36  ChrisKKK  阅读(143)  评论(0编辑  收藏  举报