莫队算法

时间复杂度:不修改的时间复杂度 为n*logn 修改的n*pow(n,0.6666)

思想: 优化的暴力,对询问进行离线化的操作,减少一些重复的元素的时间复杂度。

应用:

  • 针对某些以元素大小为单位的大量询问,给出了,l 和 r。
  • 其实看到 l,r 就可以想想是不是线段树,莫队,树状数组啥的。

 

一般莫队:

 

#include <bits/stdc++.h>
using namespace std;
#define ri register int
#define M 100055

template <class G > void read(G &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9'){f|=ch=='-';ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return ;
}
struct dian{
    int l,r,pos,id;
    bool operator <(const dian &t)const
    {
        if(pos==t.pos&&r==t.r) return l<t.l;
        if(pos==t.pos) return r<t.r;
        return pos<t.pos;
    }
}p[M];

int n,m;
int val[M],num[M];
int ANS[M];
int ans=0;
void add(int a)
{
    a=val[a];num[a]++;
    if(num[a]==1) ans++;
}
void del(int a)
{
    a=val[a];num[a]--;
    if(num[a]==0) ans--;
}
int main(){
    
    read(n);read(m);
    for(ri i=1;i<=n;i++)
    {
        read(val[i]);
    }
    int d=sqrt(n)+0.5;
    for(ri i=1;i<=m;i++)
    {
        read(p[i].l);read(p[i].r);
        p[i].id=i;
        p[i].pos=p[i].l/d;
    }
    sort(p+1,p+1+m); 
    int l=1,r=1;
    add(r);       //初始化 
    for(ri i=1;i<=m;i++)
    {
        while(l<p[i].l) del(l++);
        while(r>p[i].r) del(r--);
        while(l>p[i].l) add(--l);
        while(r<p[i].r) add(++r);
        ANS[p[i].id]=ans;
    } // 写的时候收缩和扩张,分开写,检查的时候,利用while看是++还是--,在前在后,看扩张和收缩。 
    for(ri i=1;i<=m;i++)
    {
        printf("%d\n",ANS[i]);
    }
    return 0;
    
    
} 
View Code

 

 

修改莫队:

struct dian{
    int l,r,pos,id,tim;
    bool operator <(const dian &t)const 
    {
        if(pos==t.pos) 
          {
              if(r/d==t.r/d) return tim<t.tim;
              return r<t.r;
          }
          return l<t.l;
    }
}p[M];
void work (int v,int k)
{  
    vis[cent[v]]--;
    cent[v]+=k;
    vis[cent[v]]++;
}
void work_tim(int ti,int i)
{
    if(p[i].l<=gai[ti].mubiao&&gai[ti].mubiao<=p[i].r)
        {
            work(val[gai[ti].mubiao],-1);
            work(gai[ti].VAL,1);
        }
        swap(val[gai[ti].mubiao],gai[ti].VAL);
}
d=pow(n,0.6666);
    for(ri i=1;i<=m;i++)
    {   int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        if(a==1)
        {
            p[++trmp].l=b;
            p[trmp].r=c;
            p[trmp].id=trmp;p[trmp].tim=sj;
            p[trmp].pos=p[trmp].l/d;
        }
        else
        {
          gai[++sj].mubiao=b;
          tt[n+sj]=gai[sj].VAL=c;    
        }
    }
    sort(p+1,p+trmp+1);
    sj=0;
    for(ri i=p[1].l;i<=p[1].r;i++)
    {    
         work(val[i],1);      
    }
    int baba;
    while(sj<p[1].tim) work_tim(++sj,1);
    while(sj>p[1].tim) work_tim(sj--,1);  // li hai
    for (ANS[p[1].id]=1;vis[ANS[p[1].id]]>0;ANS[p[1].id]++);
    for(ri i=2;i<=m;i++)
    {
        int l=p[i-1].l;
        int r=p[i-1].r;
        
        while(l<p[i].l) work(val[l++],-1);
        while(r>p[i].r) work(val[r--],-1);
        while(l>p[i].l) work(val[--l],1);
        while(r<p[i].r) work(val[++r],1);
        while(sj<p[i].tim) work_tim(++sj,i);
         while(sj>p[i].tim) work_tim(sj--,i);  
        //跟新ans 
        } 
View Code

 

 注意 d的取值为什么有时取 pow(n,0.6666)呢 而不是 int(0.5+sqrt(n))

后记:

  • 写主要代码的时候 收缩和扩张分开写,检查的时候,利用while看是++还是--,在前在后,看扩张和收缩。 
  • 初始化 l=r=1,add(r),就行啦

 

posted @ 2019-10-20 21:19  VxiaohuanV  阅读(142)  评论(0编辑  收藏  举报