莫队算法入门(暴力而不失优雅)

初学者建议观看:

传送门

这里有一个关于块大小的优化和奇偶性优化

块大小优化

好吧,在写这个之前,我从机房巨佬空中得到了一个结论莫队的复杂度是
(S为块大小)
但实际上是

证明略
故我们可以适当的调大块的大小
由爆OJ得,本题块大小应当在左右(不适用所有程序)

奇偶性优化

若上一块中的右端点坐标是递增的,则这块中右端点递减
若上一块中的右端点坐标是递减的,则这块中右端点递增
这样的话,原本在块转移时右端点的移动情况由

变为

故变得更优

例如:

例如样例

1 2
1 100
11 100
11 20

若使用奇偶性优化
1,2——>1,100——>11,100——>11,20
不使用
1,2——>1,100——>11,20——>11,100

可以看出还是有很大的优化的

 

int cmp(query a, query b) {
    return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}

 

板子题目:这个是弱化版的

莫队的优化2:大佬说的会快一点

 

 

Luogu P1972 [SDOI2009]HH的项链

 

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include<iostream>
using namespace std;
template <typename Tp>
void read(Tp &x){//read(n);
    x=0;char ch=1;int fh;
    while(ch!='-'&&(ch>'9'||ch<'0')){
        ch=getchar();
    }
    if(ch=='-'){
        fh=-1;ch=getchar();
    }else fh=1;
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
    }
    x*=fh;
}
inline void write(int x){if(x>9) write(x/10);putchar(x%10+48);return;}
const int maxn=1e6+100;
int n;
int block,tot;
int a[maxn],res[maxn];
int belong[maxn];
int cnt[maxn];
struct node{
    int l,r;
    int id;
    bool operator<(const node&x) const{return belong[l]^belong[x.l]?l<x.l:belong[x.l]&1?r<x.r:r>x.r;}
}q[maxn];
int ans=0;
int m;
//bool cmp(node x,node y){
//    if(belong[x.l]!=belong[y.l]){
//        return belong[x.l]<belong[y.l]; 
//    }
//    else{
//        if(belong[x.l]&1){
//            return x.r<y.r;
//        }
//        else{
//            return x.r>y.r;
//        }
//    }
//}
void add(int pos){
    if(++cnt[a[pos]]==1){
        ans++;
    }
    return ;
}

void remove(int pos){
    if(--cnt[a[pos]]==0){
        ans--;
    }
    return ; 
}
int main(){
    read(n);
    block=sqrt(n);
    for(register int i=1;i<=n;++i){
        read(a[i]);
        belong[i]=(i-1)/block+1;
    } 
    read(m);
    for(register int i=1;i<=m;++i){
        read(q[i].l);
        read(q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1);
    int ql,qr;
    for(register int i=1,l=1,r=0;i<=m;++i){
        ql=q[i].l,qr=q[i].r;
        while(l<ql){
            remove(l++);
        }    
        while(l>ql){
            add(--l);
        } 
        while(r>qr){
            remove(r--);
        } 
        while(r<qr){
            add(++r);
        }
        res[q[i].id]=ans;
    }
    for(register int i=1;i<=m;++i){
        //printf("%d\n",res[i]); 
        write(res[i]);
        putchar(10);
    }
} 

 

 

例题二:

传送门:

 

 

第一行三个整数 n,m,kn,m,k。

第二行 nn 个整数,表示 小B 的序列。

接下来的 mm 行,每行两个整数 l,rl,r。

输出格式

输出 mm 行,每行一个整数,对应一个询问的答案。

输入输出样例

输入 #1
6 4 3
1 3 2 1 1 3
1 4
2 6
3 5
5 6
输出 #1
6
9
5
2

说明/提示

【数据范围】
对于 100% 的数据,1n,m,k5×104。

#include<iostream>
#include<algorithm>
#include<math.h>
typedef long long ll; 
using namespace std;
const int maxn=1e6+100;
int belong[maxn],a[maxn];
int block;
int n,m,k;
int l=1,r=0; 
int cnt[maxn];
ll ans=0;
struct node{
    int l,r;
    int id;
}q[maxn];
ll res[maxn];
bool cmp(node x,node y){
    if(belong[x.l]^belong[y.l]){
        return x.l<y.l;
    }
    else{
        if(belong[x.l]&1){
            return x.r<y.r;
        }
        else{
            return x.r>y.r;
        }
    }
}
void del(int pos){
    ans-=(cnt[a[pos]]*cnt[a[pos]]);//就是先减去在加上 
    --cnt[a[pos]];
    ans+=(cnt[a[pos]]*cnt[a[pos]]);
}
void add(int pos){
    ans-=(cnt[a[pos]]*cnt[a[pos]]);
    ++cnt[a[pos]];
    ans+=(cnt[a[pos]]*cnt[a[pos]]);
} 
int main(){
    scanf("%d%d%d",&n,&m,&k);
    block=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        belong[i]=(i-1)/block+1;
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    // ql     qr
    for(int i=1;i<=m;i++){
        int ql=q[i].l,qr=q[i].r;
        while(l<ql){
            del(l++);//删除的这个也要删除 
        } 
        while(l>ql){
            add(--l);
        }
        while(r>qr){
            del(r--); 
        }
        while(r<qr){
            add(++r);
        }
        res[q[i].id]=ans;
    }
    for(int i=1;i<=m;i++){
        printf("%d\n",res[i]);
    }
} 

 

求区间里任取两个数是相同的概率

 

 

#include<iostream>
#include<algorithm>
#include<math.h>
template <typename Tp>
void read(Tp &x){//read(n);
    x=0;char ch=1;int fh;
    while(ch!='-'&&(ch>'9'||ch<'0')){
        ch=getchar();
    }
    if(ch=='-'){
        fh=-1;ch=getchar();
    }else fh=1;
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
    }
    x*=fh;
}
typedef long long ll;
using namespace std;
const int maxn=1e6+100;
ll gcd(ll a,ll b){
    if(b==0){
        return a;
    }
    return gcd(b,a%b); 
} 
struct node{
    ll l,r;
    int id;
}q[maxn];
ll block,n,m;
ll belong[maxn],cnt[maxn],a[maxn];
ll l=1,r=0,ans=0;
bool cmp(node x,node y){
    if(belong[x.l]^belong[y.l]){
        return x.l<y.l;
    }
    else{
        if(belong[x.l]&1){
            return x.r<y.r;
        } 
        else{
            return x.r>y.r;
        }
    }
} 
void add(int pos){
    ans-=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2);
    ++cnt[a[pos]];
    ans+=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2);
}
void del(int pos){
    ans-=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2);
    --cnt[a[pos]];
    ans+=(ll)((cnt[a[pos]])*(cnt[a[pos]]-1)/2);
}

bool cmp1(node x,node y){
    return x.id<y.id;
} 

int main(){
    read(n),read(m);
//    block=sqrt(n);
    block=n/sqrt(m*2/3);//可能会快一点 
    for(register int i=1;i<=n;i++){
        read(a[i]);
        belong[i]=(i-1)/block+1;
    } 
    for(register int i=1;i<=m;i++){
        read(q[i].l),read(q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp); 
    // ql     qr
    for(register int i=1;i<=m;i++){
        int ql=q[i].l,qr=q[i].r;
        while(l<ql){
            del(l++);
        }
        while(l>ql){
            add(--l);
        } 
        while(r>qr){
            del(r--);
        }
        while(r<qr){
            add(++r);
        }
        if(qr==ql){
            q[i].l=0;
            q[i].r=1;
            continue;
        }
        ll p=(ll)(qr-ql+1)*(qr-ql)/2;
        ll d=gcd(p,ans);
        q[i].l=ans/d;
        q[i].r=p/d;
    }
    sort(q+1,q+m+1,cmp1);
    for(register int i=1;i<=m;i++){
        printf("%lld/%lld\n",q[i].l,q[i].r);
    }
} 

 

 

 

例三:

传送门

 

 

输入复制
10 5
1 1 1 1 1 2 2 2 2 2
4 7 2
4 7 3
4 8 2
4 8 3
3 8 3
输出复制
0
2
1
1
0

 

 

 

主要是要离散化(两种离散化的方法都一样)

 把数离散化之后再暴力求解,其实莫队还是维护的区间每个值的个数

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <iostream>
#include <algorithm>
#include <math.h>
template <typename Tp>
void read(Tp &x) { //read(n);
    x = 0;
    char ch = 1;
    int fh;

    while (ch != '-' && (ch > '9' || ch < '0')) {
        ch = getchar();
    }

    if (ch == '-') {
        fh = -1;
        ch = getchar();
    } else
        fh = 1;

    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }

    x *= fh;
}

typedef long long ll;
using namespace std;
const int maxn = 1e6 + 100;
int gcd(int a, int b) {
    if (b == 0) {
        return a;
    }

    return gcd(b, a % b);
}
struct node {
    int l, r, k;
    int id;
} q[maxn];
int block, n, m, nn, len = 0;
int ql, qr, k;
int belong[maxn], cnt[maxn], a[maxn], res[maxn], mp[maxn], b[maxn];
int l = 1, r = 0, ans = 0;
bool cmp(node x, node y) {
    if (belong[x.l]^belong[y.l]) {
        return x.l < y.l;
    } else {
        if (belong[x.l] & 1) {
            return x.r < y.r;
        } else {
            return x.r > y.r;
        }
    }
}
void add(int pos) {
    ++cnt[a[pos]];
}

void del(int pos) {
    --cnt[a[pos]];
}
void inint() {
    sort(b + 1, b + n + 1);
    len = unique(b + 1, b + n + 1) - (b + 1);

    for (int i = 1; i <= n; i++) {
        a[i] = lower_bound(b + 1, b + len + 1, a[i]) - b;
    }

    //  cout<<len<<endl;
}
int x;
int getint() {
    read(x);

    if (mp[x] != 0) {
        return mp[x];
    } else {
        mp[x] = ++len;
        return mp[x];
    }
}
int main() {
    read(n), read(m);
    //  block=sqrt(n);
    block = n / sqrt(m * 2 / 3); //可能会快一点

    for (register int i = 1; i <= n; ++i) {
        read(a[i]);
        //      a[i]=getint();
        b[i] = a[i];
        belong[i] = (i - 1) / block + 1;
    }

    inint();

    for (register int i = 1; i <= m; ++i) {
        read(q[i].l), read(q[i].r), read(q[i].k);
        q[i].id = i;
    }

    sort(q + 1, q + m + 1, cmp);

    for (register int i = 1; i <= m; ++i) {
        ql = q[i].l, qr = q[i].r, k = q[i].k;

        while (l < ql) {
            del(l++);
        }

        while (l > ql) {
            add(--l);
        }

        while (r > qr) {
            del(r--);
        }

        while (r < qr) {
            add(++r);
        }

        for (register int j = 1; j <= len; ++j) {
            if (!cnt[j]) {
                continue;
            }

            if (gcd(k, cnt[j]) == 1) {
                ++res[q[i].id];
            }
        }
    }

    for (register int i = 1; i <= m; ++i) {
        printf("%d\n", res[i]);
    }
}

 

 

posted @ 2020-12-02 23:07  lipu123  阅读(183)  评论(0编辑  收藏  举报