跟着biubiubiu学算法之 莫队算法

莫队算法 第一次接触到这个题目应该是黑龙江省赛 和东北赛 当时想着一定要学会这个算法 结果拖了四个月

今天开始学莫队算法

首先 莫队算法是为了解决离线查询的题目 我们知道 很多区间问题线段树可以实现 但是让我们头疼的是一些信息线段树很快实现

这时候我们就需要用到莫队算法 莫队算法的好处就是他首先一个区间【L,R】 可以用【L,R+1】,【L-1,R】【L+1,R】【L,R-1】

O(1)知道 那么你要求的区间不过就是 区间的曼哈顿距离?可以建曼哈顿最小距离树

但是我们为啥不用最小曼哈顿距离树的思想(取最近的8*n)放在区间上 按照查询的分块排序后 可以实现最小的曼哈顿距离

时间复杂度 是 O(n^1.5)

模板

​
//如果程序可以从[L,R+1],[L-1,R],[L+1,R],[L,R-1]得到 可以用莫队
//注意先扩张后收缩
//莫队算法主要用来离线查询
ll a[MAX_N],pos[MAX_N],ans[MAX_N];
struct node {int l,r,id;}Q[MAX_N];
bool cmp(node a,node b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
int L = 0,R = 0;//多组要重置
ll Ans = 0;
void add(int x);
void del(int x);
int main(){
    scanf("%d%d%d",&n,&m,&k);
    int sz = sqrt(n);
    for(int i = 1;i<=n;++i){
        scanf("%lld",&a[i]);
        pos[i] = i/sz;
    }
    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,cmp);
    for(int i = 1;i<=m;++i){
        while(R<Q[i].r){
            R++;
            add(R);
        }
        while(L>Q[i].l){
            L--;
            add(L);
        }
        while(L<Q[i].l){
            del(L);
            L++;
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        ans[Q[i].id] = Ans;
    }
    for(int i = 1;i<=m;++i)
        printf("%lld\n",ans[i]);
    return 0;
}

​

莫队第一题 Codeforces 617 E

题意 要你求一个区间内有多少连续异或的数等于k

我们考虑异或的性质 前缀和异或 i - j 就是j - i-1前缀和

并且观察L R 容易从 四个区间推过来 就可以上莫队了

Trick 1 异或加起来可能会很大 

flag[0] = 1是初始条件 因为不取就是 1 了

代码如下

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
using namespace std;
typedef long long ll;
const int MAX_N = 1<<20;
    int n,m,k;
ll a[MAX_N],flag[MAX_N],pos[MAX_N],ans[MAX_N];
ll Ans=0;
int L = 1,R = 0;
void add(int x){
    Ans+=flag[a[x]^k];
    flag[a[x]]++;
}
void del(int x){
    flag[a[x]]--;
    Ans-=flag[a[x]^k];
}

struct node{
    int l,r,id;
}Q[MAX_N];
bool cmp(node a,node b){
    if(pos[a.l] ==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}

int main(){

    scanf("%d%d%d",&n,&m,&k);
    int blo = sqrt(n);
    for(int i = 1;i<=n;++i){
        scanf("%lld",&a[i]);
        a[i]^=a[i-1];
        pos[i] = i/blo;
    }
    for(int i = 1;i<=m;++i){
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id = i;
    }
    flag[0] = 1;
    sort(Q+1,Q+1+m,cmp);
    for(int i = 1;i<=m;++i){
        while(L<Q[i].l){
            del(L-1);
            L++;
        }
        while(L>Q[i].l){
            L--;
            add(L-1);
        }
        while(R<Q[i].r){
            R++;
            add(R);
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        ans[Q[i].id] = Ans;
    }
    for(int i = 1;i<=m;++i)
        printf("%lld\n",ans[i]);
    return 0;
}

莫队第二题 BZOJ 2038 小Z的袜子

这题我们不难知道分母就是C(2,len)  那么我们可以推一下组合式子就知道化简一下可得add和del的贡献就是加和减flag[a[x]]

#include<stdio.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1e5+5;

struct data
{
    int l,r,id;
}Q[maxn];
int pos[maxn];
int a[maxn];

bool cmp(const data &a,const data &b)
{
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}

long long  ans[maxn];
long long  ans2[maxn];
int flag[maxn];
int L=1,R=0;//由于第一个删除0的操作对答案有影响,所以可以直接L=1开始。
long long  Ans=0;
long long gcd_(long long a,long long b)
{
    return b==0?a:gcd_(b,a%b);
}
void add(int x)//先统计再加
{
    Ans+=flag[a[x]];
    flag[a[x]]++;
}

void del(int x)//先减再统计
{
    flag[a[x]]--;
    Ans-=flag[a[x]];
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    int sz=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pos[i]=i/sz;
    }
    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,cmp);
    for(int i=1;i<=m;i++)
    {
        while(R<Q[i].r)
        {
            R++;
            add(R);
        }
        while(L>Q[i].l)
        {
            L--;
            add(L);
        }
        while(L<Q[i].l)
        {
            del(L);
            L++;
        }
        while(R>Q[i].r)
        {
            del(R);
            R--;
        }
        ans[Q[i].id]=Ans;
        ans2[Q[i].id]=(1LL*(Q[i].r-Q[i].l+1)*(Q[i].r-Q[i].l))/2;
        long long tmp = gcd_(ans[Q[i].id],ans2[Q[i].id]);
        ans[Q[i].id]/=tmp;
        ans2[Q[i].id]/=tmp;
        if(ans[Q[i].id]==0) ans2[Q[i].id]=1;
    }
    for(int i=1;i<=m;i++)
        printf("%lld/%lld\n",ans[i],ans2[i]);
    return 0;
}

SPOJ的一道莫队

题意 问你区间内有多少不同的数字

算是模板题了 只要flag[a[x]]等于1并且相减 那么答案要减去1 如果flag[a[x]]等于0并且相加 那么答案要加上1

/*
DQUERY - D-query
*/
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAX_N = 1000024;
typedef long long ll;
int pos[MAX_N],flag[MAX_N],a[MAX_N];
ll ans[MAX_N];
struct node{int l,r,id;}Q[MAX_N];
ll Ans = 0;
int L = 1,R = 0;
bool cmp(const node &a,const node &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
void add(int x){
    if(!flag[a[x]]){
        Ans++;
    }
    flag[a[x]]++;
}
void del(int x){
    if(flag[a[x]]==1){
        Ans--;
    }
    flag[a[x]]--;
}
int main(){
    int n,m;
    scanf("%d",&n);
    int blo = sqrt(n);
    for(int i = 1;i<=n;++i){
        scanf("%d",&a[i]);
        pos[i] = i/blo;
    }
    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,cmp);
    for(int i = 1;i<=m;++i){
        while(R<Q[i].r){
            R++;
            add(R);
        }
        while(L>Q[i].l){
            L--;
            add(L);
        }
        while(L<Q[i].l){
            del(L);
            L++;
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        ans[Q[i].id] = Ans;
    }
    for(int i = 1;i<=m;++i)
        printf("%lld\n",ans[i]);
    return 0;
}

第四题 Codeforeces 86 D

题意就是一段区间内的价值为一个数出现的次数的平方乘上他自己 我们就可以很简单的根据莫队来推add 和 del了 

没错 叫平方差公式

/*
codeforces 86 D
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
using namespace std;
const int MAX_N = 1000024;
typedef long long ll;
int a[MAX_N],pos[MAX_N];
ll flag[MAX_N],ans[MAX_N];
ll Ans = 0;
int L = 1,R = 0;
struct node{int l,r,id;}Q[MAX_N];
bool cmp(const node &a,const node &b){
    if(pos[a.l]==pos[b.l]) return a.r< b.r;
    return pos[a.l]<pos[b.l];
}
void add(int x){
    Ans+=a[x]*(flag[a[x]]*2+1);
    flag[a[x]]++;
}
void del(int x){
     Ans-=a[x]*((flag[a[x]]-1)*2+1);
    flag[a[x]]--;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int blo = sqrt(n);
    for(int i = 1;i<=n;++i){
        scanf("%d",&a[i]);
        pos[i] = i/blo;
    }
    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,cmp);
    for(int i = 1;i<=m;++i){
        while(R<Q[i].r){
            R++;
            add(R);
        }
        while(L>Q[i].l){
            L--;
            add(L);
        }
        while(L<Q[i].l){
            del(L);
            L++;
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        ans[Q[i].id] = Ans;
    }
    for(int i = 1;i<=m;++i)
        printf("%I64d\n",ans[i]);
    return 0;
}

第五题 HDOJ 5213 Lucky

题意是给你好多数 然后给你两个区间 一左一右 你分别从区间内取一个数 加起来等于k 这两个区间我们该如何操作呢?

其实不难想到利用容斥 进行容斥容易明白[L1,R1] + [L2,R2]的答案为[L1 , R2 ]- [L1 - L2-1]-[L2 , R2-1] + [R1+1,L2-1]

便可以操作了

这是第一个版本

/*
hdoj 5213 Lucky
*/
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <sstream>
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
using namespace std;
const int MAX_N = 2e5+5;
typedef long long ll;
int a[MAX_N],pos[MAX_N];
ll ans[MAX_N],flag[MAX_N],flag_l[MAX_N],flag_r[MAX_N],flag_m[MAX_N];
ll Ans= 0,Ans_l = 0,Ans_r = 0,Ans_m=0;
int n,k,m;
int L = 1,R = 0,L_l = 1,R_l = 0,L_r = 1,R_r = 0,L_m = 1,R_m = 0;
void add(int x){
      if(k>a[x]&&k-a[x]<n)
    Ans+=flag[k-a[x]];
    flag[a[x]]++;
}
void del(int x){
     flag[a[x]]--;
     if(a[x]<k&&k-a[x]<=n)
    Ans-=flag[k-a[x]];

}
void add_l(int x){
      if(k>a[x]&&k-a[x]<=n)
    Ans_l+=flag_l[k-a[x]];
    flag_l[a[x]]++;
}
void del_l(int x){
      flag_l[a[x]]--;
      if(a[x]<k&&k-a[x]<=n)
    Ans_l-=flag_l[k-a[x]];

}
void add_r(int x){
    if(k>a[x]&&k-a[x]<=n)
    Ans_r+=flag_r[k-a[x]];
    flag_r[a[x]]++;
}
void del_r(int x){
     flag_r[a[x]]--;
     if(a[x]<k&&k-a[x]<=n)
    Ans_r-=flag_r[k-a[x]];
}
void add_m(int x){

      if(k>a[x]&&k-a[x]<=n)
    Ans_m+=flag_m[k-a[x]];
      flag_m[a[x]]++;
}
void del_m(int x){
       flag_m[a[x]]--;
       if(a[x]<k&&k-a[x]<=n)
    Ans_m-=flag_m[k-a[x]];

}
ll ans1[MAX_N],ans2[MAX_N],ans3[MAX_N],ans4[MAX_N];
struct node {int l,r,id;}Q1[MAX_N],Q2[MAX_N],Q3[MAX_N],Q4[MAX_N],Q[MAX_N],T[MAX_N];
bool cmp(const node &a,const node  &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
int main(){

    while(scanf("%d",&n)!=EOF){
    L = 1,R = 0,L_l = 1,R_l = 0,L_r = 1,R_r = 0,L_m = 1,R_m = 0;
    Ans= 0,Ans_l = 0,Ans_r = 0,Ans_m=0;
    for(int i = 1;i<=n;++i){
        flag_l[i] = flag_m[i] = flag_r[i] = flag[i] = 0;
    }
    scanf("%d",&k);
    int blo = sqrt(n);
    for(int i = 1;i<=n;++i){
        scanf("%d",&a[i]);
        pos[i] = i/blo;
    }
    scanf("%d",&m);
    for(int i = 1;i<=m;++i){
        scanf("%d%d%d%d",&Q[i].l,&Q[i].r,&T[i].l,&T[i].r);
        Q1[i].l = Q[i].l,Q1[i].r = T[i].r,Q1[i].id = i;
        Q2[i].l = Q[i].l,Q2[i].r = T[i].l-1,Q2[i].id = i;
        Q3[i].l = Q[i].r+1,Q3[i].r = T[i].r,Q3[i].id = i;
        Q4[i].l = Q[i].r+1,Q4[i].r = T[i].l-1,Q4[i].id = i;
    }
    sort(Q1+1,Q1+1+m,cmp);
    sort(Q2+1,Q2+1+m,cmp);
    sort(Q3+1,Q3+1+m,cmp);
    sort(Q4+1,Q4+1+m,cmp);
    for(int i = 1;i<=m;++i){
        while(R<Q1[i].r){
            R++;
            add(R);
        }
        while(L>Q1[i].l){
            L--;
            add(L);
        }
        while(L<Q1[i].l){
            del(L);
            L++;
        }
        while(R>Q1[i].r){
            del(R);
            R--;
        }
        ans1[Q1[i].id] = Ans;
        dbg(Ans);
          while(R_l<Q2[i].r){
            R_l++;
            add_l(R_l);
        }
        while(L_l>Q2[i].l){
            L_l--;
            add_l(L_l);
        }
        while(L_l<Q2[i].l){
            del_l(L_l);
            L_l++;
        }
        while(R_l>Q2[i].r){
            del_l(R_l);
            R_l--;
        }
        ans2[Q2[i].id] = Ans_l;
        dbg(Ans_l);
          while(R_r<Q3[i].r){
            R_r++;
            add_r(R_r);
        }
        while(L_r>Q3[i].l){
            L_r--;
            add_r(L_r);
        }
        while(L_r<Q3[i].l){
            del_r(L_r);
            L_r++;
        }
        while(R_r>Q3[i].r){
            del_r(R_r);
            R_r--;
        }
        ans3[Q3[i].id] = Ans_r;
        dbg(Ans_r);
           while(R_m<Q4[i].r){
            R_m++;
            add_m(R_m);
        }
        while(L_m>Q4[i].l){
            L_m--;
            add_m(L_m);
        }
        while(L_m<Q4[i].l){
            del_m(L_m);
            L_m++;
        }
        while(R_m>Q4[i].r){
            del_m(R_m);
            R_m--;
        }
        ans4[Q4[i].id] = Ans_m;
    }
    for(int i = 1;i<=m;++i){
        printf("%lld\n",ans1[i]-ans2[i]-ans3[i]+ans4[i]);
    }
    }
    return 0;
}

这是修改后的第二个版本

/*
HDOJ 5213 LUCKY new
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
using namespace std;
int n,m,k;
const int MAX_N = 2e5+5;
typedef long long ll;
int pos[MAX_N],a[MAX_N];
ll ans[4][MAX_N],flag[4][MAX_N],Ans[4];
struct node {int l,r,id,belong;}Q[MAX_N<<1];
bool cmp(const node &a,const node &b){
    if(pos[b.l]==pos[a.l]) return a.r<b.r;
    return pos[a.l] < pos[b.l];
}
int L[4],R[4];
void add(int x,int p){
    if(a[x]<k&&k-a[x]<=n)
        Ans[p]+=flag[p][k-a[x]];
    flag[p][a[x]]++;
}
void del(int x,int p){
    if(a[x]<k&&k-a[x]<=n)
        Ans[p]-=flag[p][k-a[x]];
    flag[p][a[x]]--;
}
int main(){
    while(scanf("%d%d",&n,&k)!=EOF){
    int blo = sqrt(n);
    for(int i = 0;i<4;++i){
        Ans[i] = 0;
        L[i] = 1;
        R[i] = 0;
    }
    for(int i = 1;i<=n;++i){
        scanf("%d",&a[i]);
        pos[i] = i/blo;
        for(int j = 0;j<4;++j)
         flag[j][i] = 0;
    }
    scanf("%d",&m);
    int cnt = 1;
    for(int i = 1;i<=m;++i){
        int l1,l2,r1,r2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        Q[cnt].l = l1,Q[cnt].r = r2,Q[cnt].id = i,Q[cnt++].belong = 0;
        Q[cnt].l = l1,Q[cnt].r = l2-1,Q[cnt].id = i,Q[cnt++].belong = 1;
        Q[cnt].l = r1+1,Q[cnt].r = r2,Q[cnt].id = i,Q[cnt++].belong = 2;
        Q[cnt].l = r1+1,Q[cnt].r = l2-1,Q[cnt].id = i,Q[cnt++].belong = 3;
    }

    cnt--;
    sort(Q+1,Q+1+cnt,cmp);
    for(int i = 1;i<=cnt;i++){
        while(R[Q[i].belong]<Q[i].r){
            R[Q[i].belong]++;
            add(R[Q[i].belong],Q[i].belong);
        }
        while(L[Q[i].belong]>Q[i].l){
            L[Q[i].belong]--;
            add(L[Q[i].belong],Q[i].belong);
        }
        while(L[Q[i].belong]<Q[i].l){
            del(L[Q[i].belong],Q[i].belong);
            L[Q[i].belong]++;
        }
        while(R[Q[i].belong]>Q[i].r){
            del(R[Q[i].belong],Q[i].belong);
            R[Q[i].belong]--;
        }
        dbg(Q[i].belong);
        ans[Q[i].belong][Q[i].id] = Ans[Q[i].belong];
    }
    for(int i = 1;i<=m;++i){
        dbg(ans[0][i]);
        dbg(ans[1][i]);
        dbg(ans[2][i]);
        dbg(ans[3][i]);
        printf("%lld\n",ans[0][i]-ans[1][i]-ans[2][i]+ans[3][i]);
    }
    }
    return 0;
}

莫队第六题 FZU 2226 信心题

这题的题意是给你N个数 问你 L-R个区间一样的数距离最大的距离是多少

我们可以用vector存坐标 在读入的时候直接放进vector

然后用莫队开两个数组维护左下标 与右下标 

注意右下标初始化要为-1 作下标为0 防止设为0 0 的时候第一个数R++

左右下标就变成 0 1有两个下标至少有两个数 这是不正确的 因为我们假设只通过一个数

为什么只动r呢 因为这题l r r在l右边才是合法状态 看代码吧

/*
FZU 2226
*/
#include <cstdio>
#include <cstring>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
const int MAX_N = 500024;
typedef long long ll;
int a[MAX_N],pos[MAX_N],cnt_l[1024],cnt_r[1024];
ll ans[MAX_N];
int L,R;
vector<int > vt[1024];
struct node {
    int l,r,id;
}Q[MAX_N];
ll Ans;
bool cmp(const node &a,const node &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
int main(){
    int n,m;
    while(~scanf("%d",&n)){
          for(int i = 1;i<=1000;++i){
            cnt_l[i] = 0;
            cnt_r[i] = 0;
          vt[i].clear();
          }
          L=1,R=0,Ans = 0;
    int blo = sqrt(n);
    for(int i = 1;i<=n;++i){
        scanf("%d",&a[i]);
        vt[a[i]].push_back(i);
        pos[i] = i / blo;

    }
    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,cmp);
    for(int i= 1;i<=m;++i){
        while(R<Q[i].r){
            R++;
            cnt_r[a[R]]++;
        }
        while(L>Q[i].l){
            L--;
            cnt_l[a[L]]--;
        }
        while(L<Q[i].l){
            cnt_l[a[L]]++;
            L++;
        }
        while(R>Q[i].r){
            cnt_r[a[R]]--;
            R--;
        }
        int maxx =0;
        for(int j = 1;j<=1000;j++){
            if(cnt_r[j]<1) continue;
            dbg(cnt_l[j]);
            int tmp1 = vt[j][cnt_r[j]-1];
            int tmp2 = vt[j][cnt_l[j]];
            maxx = max(maxx,tmp1-tmp2);
        }
        ans[Q[i].id] = maxx;
    }
    for(int i = 1;i<=m;++i)
        printf("%lld\n",ans[i]);
    }
    return 0;
}

 莫队第七题 莫队第七题Groups

考虑添加数和删除数判断左右 具体看代码吧

/*
hdoj 4638
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
typedef long long ll;
const int MAX_N = 2000024;
int a[MAX_N],pos[MAX_N],flag[MAX_N];
struct node {
    int l,r,id;
}Q[MAX_N];
ll ans[MAX_N];
bool cmp(const node &a,const node &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l] < pos[b.l];
}
int L = 1,R = 0;
ll Ans = 0;
void add(int x){
    if((!flag[a[x]-1])&&(!flag[a[x]+1])) Ans++;
    else if((flag[a[x]-1])&&(flag[a[x]+1])) Ans--;
    flag[a[x]]++;
}
void del(int x){
    if((!flag[a[x]-1])&&(!flag[a[x]+1])) Ans--;
    else if((flag[a[x]-1])&&(flag[a[x]+1])) Ans++;
    flag[a[x]]--;
}

int main(){
    int t;
    scanf("%d",&t);
    while(t--){
     int n,m;
     scanf("%d%d",&n,&m);
     int blo = sqrt(n);
     L = 1,R = 0,Ans = 0;
     for(int i = 1;i<=n;++i){
        scanf("%d",&a[i]);
        pos[i] = i/blo;
        flag[i] = 0;
     }
     flag[n+1] = 0;
     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,cmp);
     for(int i = 1;i<=m;++i){
        while(R<Q[i].r){
            R++;
            add(R);
            dbg(Ans);
        }
        while(L>Q[i].l){
            L--;
            add(L);
        }
        while(L<Q[i].l){
            del(L);
            L++;
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        dbg(Ans);
        ans[Q[i].id] = Ans;
     }
     for(int i = 1;i<=m;++i)
        printf("%lld\n",ans[i]);
    }
    return 0;
}

莫队第八题 HDOJ 4676

这题我们得知道一个公式

对一个序列的某个区间L,R,每个数两两之间的GCD之和为
        Σd|nφ(d)×C(2,num(d));   d在这里指这个区间的所有约数 

知道公式就直接写 不知道公式。。。

/*
hdoj 4676
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
const int MAX_N = 1e5+5;
struct node{
    int l,r,id;
}Q[MAX_N];
int pos[MAX_N];
int phi[MAX_N];
int pri[MAX_N];
int a[MAX_N];
int ans[MAX_N];
int Flag[MAX_N];
vector<int > v[MAX_N];
int num[MAX_N];
bool cmp(const node &a,const node &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l]<pos[b.l];
}
void Getphi(int Max){
    phi[1] = 1;
    for(int i = 2;i<=Max;++i)
    {
        if(!Flag[i]){
            phi[i] = i-1;
            pri[++pri[0]] = i;
        }
        for(int j = 1;j<=pri[0];j++){
            if(1LL*i*pri[j]>Max) break;
            Flag[i*pri[j]] = 1;
            if(i%pri[j] == 0){
                phi[i*pri[j]] = phi[i]*pri[j];
                break;
            }
            phi[i*pri[j]] = phi[i] *(pri[j] - 1);
        }
    }
    for(int i = 1;i<=Max;++i)
    {
        for(int j = i;j<=Max;j+=i)
            v[j].push_back(i);
    }
}
int L = 0,R = 0;
long long Ans = 0;
void add(int x){
    for(int i = 0;i<v[a[x]].size();i++)
        Ans+=1ll*phi[v[a[x]][i]]*num[v[a[x]][i]];
    for(int i = 0;i<v[a[x]].size();i++)
        num[v[a[x]][i]]++;
    return ;
}
void del(int x){
    for(int i = 0;i<v[a[x]].size();i++)
        num[v[a[x]][i]]--;
    for(int i = 0;i<v[a[x]].size();i++)
        Ans-=1LL*phi[v[a[x]][i]]*num[v[a[x]][i]];
    return ;
}
int main(){
    int t;
    Getphi(20000);
    scanf("%d",&t);
    for(int l = 1;l<=t;++l){
    printf("Case #%d:\n",l);
        int n;
        L = 0,R = 0,Ans = 0;

        scanf("%d",&n);    
        for(int i = 1;i<=n;++i) num[i] = 0;
        int blo = sqrt(n);
        for(int i = 1;i<=n;++i){
            scanf("%d",&a[i]);
            pos[i] = i / blo;
        }
        int T;
        scanf("%d",&T);
        for(int i = 1;i<=T;++i){
            scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].id = i;
        }
        sort(Q+1,Q+1+T,cmp);
        for(int i= 1;i<=T;i++){
            while(R<Q[i].r){
                R++;
                add(R);
            }
            while(L>Q[i].l){
                L--;
                add(L);
            }
            while(L<Q[i].l){
                del(L);
                L++;
            }
            while(R>Q[i].r){
                del(R);
                R--;
            }
            ans[Q[i].id] = Ans;
        }
        for(int i = 1;i<=T;++i)
            printf("%d\n",ans[i]);
    }
    return 0;
}

莫队第九题 NBUT 1457

用带修莫队加立方和公式解决这个问题

/*
NBUT 1457
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <sstream>
#include <cmath>
using namespace std;
const int MAX_N = 100024;
int a[MAX_N],b[MAX_N],pos[MAX_N];
struct node {int l,r,id;}Q[MAX_N];
long long flag[MAX_N];
bool cmp(const node &a,const node &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l] < pos[b.l];
}
int L = 1,R = 0;
long long Ans,ans[MAX_N];
void add(int x){
    Ans-=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
    flag[a[x]]++;
    Ans+=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
}
void del(int x){
    Ans-=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
    flag[a[x]]--;
    Ans+=1LL*flag[a[x]]*flag[a[x]]*flag[a[x]];
}
int main(){
    int n;
    while(scanf("%d",&n)==1){
        int blo = (int)sqrt(n*1.0);
        for(int i = 0;i<=100000;i++)
            flag[i] = 0;
        L = 1,R = 0,Ans = 0;
        for(int i = 1;i<=n;++i){
            scanf("%d",&a[i]);
            pos[i]=i /blo;
            b[i] = a[i];
        }
        sort(b+1,b+1+n);
        int num = unique(b+1,b+1+n) - b- 1;
        for(int i =  1;i<=n;++i)
            a[i] = lower_bound(b+1,b+1+n,a[i]) - b- 1;
        int T;
        scanf("%d",&T);
        for(int i = 1;i<=T;++i){
            scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].id = i;
        }
        sort(Q+1,Q+1+T,cmp);
        for(int i = 1;i<=T;++i){
            while(R<Q[i].r){
                R++;
                add(R);
            }
            while(L>Q[i].l){
                L--;
                add(L);
            }
            while(L<Q[i].l){
                del(L);
                L++;
            }
            while(R>Q[i].r){
                del(R);
                R--;
            }
            ans[Q[i].id] = Ans;
        }
        for(int i = 1;i<=T;++i)
            printf("%I64d\n",ans[i]);
    }
    return 0;
}

莫队第十题 cf 940 F

这题题意就是让你找区间内数的出现次数 从小到大第一个没有的数

其实可以暴力找范围 不然维护很麻烦

/*
cf 940F
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
using namespace std;
const int MAX_N = 300024;
int pos[MAX_N],col[MAX_N],b[MAX_N],ans[MAX_N],Num[MAX_N],flag[MAX_N],lst[MAX_N];
struct node {int l,r,pos,k;}q[MAX_N];
struct opti {int x,y,z;}o[MAX_N];
int L = 1,R = 0,Ans = 0,K = 0,q_num=0,o_num;
bool cmp(const node &a,const node &b){
    if(pos[a.l]^pos[b.l]) return pos[a.l]<pos[b.l];
    if(pos[a.r]^pos[b.r]) return pos[a.r]<pos[b.r];
    return a.k<b.k;
}
void add(int x){
    Num[flag[col[x]]]--;
    Num[++flag[col[x]]]++;
}
void del(int x){
    Num[flag[col[x]]]--;
    Num[--flag[col[x]]]++;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int blo = pow(n,2.0/3);
    for(int i = 1;i<=n;++i){
        scanf("%d",&col[i]);
        pos[i] = i / blo;
        b[i] = col[i];
        lst[i] = col[i];
    }
    for(int i = 1;i<=m;++i){
        int opt,x,y;
        scanf("%d%d%d",&opt,&x,&y);
        if(opt==1){
            q[++q_num].l = x,q[q_num].r= y,q[q_num].pos = q_num,q[q_num].k = o_num;
        }
        else {
            o[++o_num].x = x,o[o_num].y = y,o[o_num].z = lst[x],lst[x] = y,b[n+o_num] = y;
        }
    }
    sort(b+1,b+1+n+o_num);
    int b_num = unique(b+1,b+1+n+o_num)-b-1;
    sort(q+1,q+1+q_num,cmp);
    for(int i = 1;i<=n;i++){
        col[i] = lower_bound(b+1,b+1+b_num,col[i]) - b;
        //lst[i] = lower_bound(b+1,b+1+b_num,lst[i]) - b;
    }
    for(int i = 1;i<=o_num;i++){
        o[i].z = lower_bound(b+1,b+1+b_num,o[i].z)-b;
        o[i].y = lower_bound(b+1,b+1+b_num,o[i].y)- b;
    }
    for(int i = 1;i<=q_num;i++){
        while(K<q[i].k){
            ++K;
            if(o[K].x<=R&&o[K].x>=L){
             Num[flag[col[o[K].x]]]--;
             Num[--flag[col[o[K].x]]]++;
             Num[flag[o[K].y]]--;
             Num[++flag[o[K].y]]++;
            }
            col[o[K].x] = o[K].y;
        }
        while(K>q[i].k){
            if(o[K].x<=R&&o[K].x>=L){
             Num[flag[col[o[K].x]]]--;
             Num[--flag[col[o[K].x]]]++;
             Num[flag[o[K].z]]--;
             Num[++flag[o[K].z]]++;
            }
            col[o[K].x] = o[K].z;
            --K;
        }
        while(R<q[i].r){
            R++;
            add(R);
        }
        while(L>q[i].l){
            L--;
            add(L);
        }
        while(L<q[i].l){
            del(L);
            L++;
        }
        while(R>q[i].r){
            del(R);
            R--;
        }
        for(int i = 1;i<=2000000;i++)
          if(!Num[i]) {
            Ans = i;
            break;
        }
        ans[q[i].pos] = Ans;
    }
    for(int i = 1;i<=q_num;++i)
        printf("%d\n",ans[i]);
    return 0;
}

莫队第十一题 CF 220B

题意维护区间内出现一个数的出现个数为他本身个数有多少个 还是比较好维护的

/*
cf 220B
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <queue>
#include <stack>
#include <map>
using namespace std;
#define dbg(x) ;
//cout<<#x<<" = "<< (x)<< endl
int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
const int MAX_N = 1000024;
int a[MAX_N],b[MAX_N],ans[MAX_N],pos[MAX_N],flag[MAX_N];
struct node{int l,r,id;}Q[MAX_N];
int L = 1,R = 0,Ans;
bool cmp (const node &a, const node &b){
    if(pos[a.l]==pos[b.l]) return a.r<b.r;
    return pos[a.l] <pos[b.l];
}
void add(int x){
    if(flag[a[x]]==a[x]){
        Ans--;
    }
    flag[a[x]]++;
    if(flag[a[x]]==a[x]){
        Ans++;
    }
}
void del(int x){
    if(flag[a[x]]==a[x]){
        Ans--;
    }
    flag[a[x]]--;
    if(flag[a[x]]==a[x]){
        Ans++;
    }
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int blo = sqrt(n);
    for(int i = 1;i<=n;++i){
       a[i] = read();
        if(a[i]>=100001) a[i] = 100001;
        pos[i] = i/blo;
    }
    for(int i = 1;i<=m;++i){
        Q[i].l = read();
        Q[i].r = read();
        Q[i].id = i;
    }
    sort(Q+1,Q+1+m,cmp);
    for(int i = 1;i<=m;++i){
        while(R<Q[i].r){
            R++;
            add(R);
        }
        while(L>Q[i].l){
            L--;
            add(L);
        }
        while(L<Q[i].l){
            del(L);
            L++;
        }
        while(R>Q[i].r){
            del(R);
            R--;
        }
        ans[Q[i].id] = Ans;
    }
    for(int i = 1;i<=m;++i)
        printf("%d\n",ans[i]);
    return 0;
}

莫队第十二题 UVA 12345

想让你实现一个类集合的查找不同元素个数 

可以修改 用带修莫队维护

/*
UVA 12345
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#include <cmath>
#include <sstream>
using namespace std;
#define dbg(x) cout<<#x<<" = "<< (x)<< endl
const int MAX_N = 1000024;
int ans[MAX_N],col[MAX_N],lst[MAX_N],pos[MAX_N],flag[MAX_N];
int L = 1,R = 0,Ans = 0,K = 0,q_num,o_num;
struct node {int l ,r, id,k;}q[MAX_N];
struct opti {int l,r,z;}o[MAX_N];
bool cmp (const node &a,const node &b){
    if(pos[a.l]^pos[b.l]) return pos[a.l] < pos[b.l];
    if(pos[a.r]^pos[b.r]) return pos[a.r]<pos[b.r];
    return a.k<b.k;
}
void add(int x){
    if(!flag[col[x]]) Ans++;
    flag[col[x]]++;
}
void del(int x){
    if(flag[col[x]]==1) Ans--;
    flag[col[x]]--;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int blo = pow(n,2.0/3);
    for(int i = 1;i<=n;++i){
        scanf("%d",&col[i]);
        pos[i] = i/blo;
        lst[i] = col[i];
    }
    for(int i = 1;i<=m;++i){
        char str[5];
        int x,y;
        scanf("%s%d%d",str,&x,&y);
        x++;
        if(str[0]=='Q'){
           q_num++;
            q[q_num].l = x,q[q_num].r = y,q[q_num].id = q_num,q[q_num].k = o_num;
        }
        else {
           o_num++;
            o[o_num].l = x,o[o_num].r = y,o[o_num].z = lst[x],lst[x] = y;
        }
    }
    sort(q+1,q+1+q_num,cmp);
    for(int i = 1;i<=q_num;++i){
       while(K<q[i].k){
            ++K;
            if(o[K].l>=L&&o[K].l<=R){
                if(!--flag[col[o[K].l]]) Ans--;
                if(!flag[o[K].r]++) Ans++;
            }
            col[o[K].l] = o[K].r;
        }
        while(K>q[i].k){
            if(o[K].l>=L&&o[K].l<=R){
                if(!--flag[col[o[K].l]]) Ans--;
                if(!flag[o[K].z]++) Ans++;
            }
            col[o[K].l] = o[K].z;
            --K;
        }
        while(R<q[i].r){
            R++;
            add(R);
        }
        while(L>q[i].l){
            L--;
            add(L);
        }
        while(L<q[i].l){
            del(L);
            L++;
        }
        while(R>q[i].r){
            del(R);
            R--;
        }
        ans[q[i].id] = Ans;
    }
    for(int i = 1;i<=q_num;++i)
        printf("%d\n",ans[i]);
    return 0;
}

莫队第13题 BZOJ 2120

写了这题学会了带修莫队 放最后让你们开心下 哈哈

/*
BZOJ 2120
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
using namespace std;
const int MAX_N = 50024;
int ans[MAX_N],flag[1000024],col[MAX_N],pos[MAX_N],lst[MAX_N];
struct node {int l,r,id,pos,k;}Q[MAX_N];
struct opti {int l,r,z;}o[MAX_N];
int L = 1 ,R = 0,Ans = 0,K = 0,q_num,o_num;
bool cmp (const node &a,const node &b){
    if(pos[a.l]^pos[b.l]) return pos[a.l]<pos[b.l];
    if(pos[a.r]^pos[b.r]) return pos[a.r]<pos[b.r];
    return a.k<b.k;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int blo = pow(n,2.0/3);
    for(int i = 1;i<=n;++i){
        scanf("%d",&col[i]);
        pos[i] = i / blo;
        lst[i] = col[i];
    }
    char str[20];
    int x,y;
    for(int i = 1;i<=m;++i){
        scanf("%s%d%d",str,&x,&y);
        if(str[0]!='R'){
            Q[++q_num].l = x,Q[q_num].r = y,Q[q_num].pos = q_num,Q[q_num].k = o_num;
        }
        else {
            o[++o_num].l = x,o[o_num].r = y,o[o_num].z = lst[x],lst[x] = y;
        }
    }
    sort(Q+1,Q+1+q_num,cmp);
    for(int i = 1;i<=q_num;i++){
    while(K<Q[i].k){
        ++K;
        if(L<=o[K].l&&o[K].l<=R){
        if(!--flag[col[o[K].l]]) Ans--;
        if(!flag[o[K].r]++) Ans++;
        }
        col[o[K].l] = o[K].r;
    }
    while(K>Q[i].k){
        if(L<=o[K].l&&o[K].l<=R){
        if(!--flag[col[o[K].l]]) Ans--;
        if(!flag[o[K].z]++) Ans++;
        }
        col[o[K].l] = o[K].z;
        --K;
    }
    while(R<Q[i].r){
        R++;
        if(!flag[col[R]]++) Ans++;
    }
    while(L>Q[i].l){
        L--;
        if(!flag[col[L]]++) Ans++;
    }
    while(L<Q[i].l){
        if(!--flag[col[L]]) Ans--;
        L++;
    }
    while(R>Q[i].r){
        if(!--flag[col[R]]) Ans--;
        R--;
    }
    ans[Q[i].pos] = Ans;
    }
    for(int i = 1;i<=q_num;++i)
        printf("%d\n",ans[i]);
    return 0;
}

未完待续

猜你喜欢

转载自blog.csdn.net/heucodesong/article/details/82221363