首页 > 编程语言 > 详细

莫队算法

时间:2020-05-10 00:11:12      阅读:48      评论:0      收藏:0      [点我收藏+]

https://blog.csdn.net/niiick/java/article/details/80327593

就是用一个cnt[]数组记录当前每个数字出现了多少次
然后用左L,右R指针不断移动来获得当前区间的cnt[]数组
增加时每当有一个cnt[i]==1就代表新增加了一个数字
删除时每当有一个cnt[i]==0就代表新减少了一个数字

但是既然都说了这是一个暴力,这样当然是过不了
不过这就是莫队算法的其中一个核心了

考虑到这样做会T的原因是左右指针移动次数无法确定‘
所以我们可以改变对询问的处理顺序,采用离线算法

具体怎么改变询问顺序呢
我们将整个序列按分块思想分成sqrt(N)块,每块有sqrt(N)个元素
然后将询问排序,具体方法是
以询问左区间L所在块为第一关键字
询问右区间R为第二关键字排序

现在所有询问左区间所在块是单调递增的
所以左指针移动次数不超过M*sqrt(N)次
由于同一个块内所有询问右指针单调递增
一个块内的询问右指针移动最大为N, NN,总共sqrt(N)个块
所以右指针移动次数不超过N*?sqrt(N)次
整体复杂度O((N+M) *sqrt(N)

模板题:BZOJ1878 [SDOI2009]HH的项链

#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
 
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return x*f;
}
 
int t,n,m;
int L=1,R=0,a[500010];
int cnt[1000010];
struct node{int ll,rr,num;}q[200010];
bool cmp(node a,node b){return (a.ll/t)==(b.ll/t) ?a.rr<b.rr :(a.ll/t)<(b.ll/t);}
int ans[200010],sum;
 
void add(int x)
{
    cnt[x]++;
    if(cnt[x]==1) sum++;
}
 
void del(int x)
{
    cnt[x]--;
    if(cnt[x]==0) sum--;
}
 
int main()
{
    n=read();
    for(int i=1;i<=n;++i)
    a[i]=read();
     
    m=read();
    for(int i=1;i<=m;++i)
    q[i].ll=read(),q[i].rr=read(),q[i].num=i;
     
    t=sqrt(n);//块数
    sort(q+1,q+1+m,cmp);
    for(int i=1;i<=m;++i)
    {
        while(R<q[i].rr) add(a[++R]);
        while(R>q[i].rr) del(a[R--]);
        while(L<q[i].ll) del(a[L++]);
        while(L>q[i].ll) add(a[--L]);
        ans[q[i].num]=sum;
    }
    for(int i=1;i<=m;++i)
    printf("%d\n",ans[i]);
    return 0;
}

  

带修改的莫队
同样先看问题
Q:有一个长为N序列,有M个操作:
查询操作,查询在区间[L,R]内,出现了多少个不同的数字。
修改操作,修改第x个数字为y
(数字范围为0到1000000之间的整数),N ≤ 50000,M ≤ 50000。

带上了修改的思路依旧很暴力
对于每个询问记录在这个询问前修改了多少次
改多了或改少了都一个while改回来

不过这里分块要变成
每块大小N^(2/3) ,总共N^(1/3)块
且要加入修改顺序为第三关键字排序
这样左右指针,修改指针移动次数都是N∗N^(3/2)

所以整个算法渐进复杂度O(N^(5/3)

BZOJ2120 || 洛谷P1903 [国家集训队]数颜色
#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<‘0‘||ss>‘9‘){if(ss==‘-‘)f=-1;ss=getchar();}
    while(ss>=‘0‘&&ss<=‘9‘){x=x*10+ss-‘0‘;ss=getchar();}
    return x*f;
}

const int maxn=50010;
int n,m,t;
int a[maxn],cnt[2000010];
int cntq,cntc;
int L=1,R=0,cur,sum;
struct node{int ll,rr,num,id,pre;}q[maxn];
struct node2{int pos,val;}c[maxn];
int ans[maxn];
bool cmp(node a,node b)
{
    if(a.ll/t!=b.ll/t) return a.ll/t<b.ll/t;//左端点按块
    if(a.rr/t!=b.rr/t) 
    {
    	if((a.ll/t)&1) return a.rr<b.rr;//右端点奇偶块排序优化
    	else return a.rr>b.rr;
    }
    return a.pre<b.pre;//修改顺序排
}

void add(int x)
{
    cnt[x]++;
    if(cnt[x]==1) sum++;
}

void del(int x)
{
    cnt[x]--;
    if(cnt[x]==0) sum--;
}

void update(int cur)
{
    if(c[cur].pos>=L&&c[cur].pos<=R)//修改在当前查询区间里才要更新
    {
        if(--cnt[ a[ c[cur].pos ] ]==0) sum--;
        if(++cnt[ c[cur].val ]==1) sum++;
    }
    swap(c[cur].val,a[c[cur].pos]);//因为可能多次更改,所以直接交换要改的数
}

int main()
{
    n=read();m=read(); t=pow(n,2.0/3);//**高亮**,注意分块大小
    for(int i=1;i<=n;++i) a[i]=read();
    for(int i=1;i<=m;++i)
    {
        char ss; scanf("%s",&ss);
        if(ss==‘Q‘)
        {
            q[++cntq].ll=read(); q[cntq].rr=read();
            q[cntq].pre=cntc; //记录上一次修改编号
            q[cntq].num=cntq;
        }
        else if(ss==‘R‘)
        c[++cntc].pos=read(),c[cntc].val=read();//记录修改
    }
    
    sort(q+1,q+1+cntq,cmp);
    for(int i=1;i<=cntq;++i)
    {
        while(R<q[i].rr) add(a[++R]);
        while(R>q[i].rr) del(a[R--]);
        while(L<q[i].ll) del(a[L++]);
        while(L>q[i].ll) add(a[--L]);
        while(cur<q[i].pre) update(++cur);//把改多/少的改回来
        while(cur>q[i].pre) update(cur--);
        ans[q[i].num]=sum;
    }
    for(int i=1;i<=cntq;++i)
    printf("%d\n",ans[i]);
    return 0;
}

  

莫队算法

原文:https://www.cnblogs.com/cutemush/p/12861029.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!