zoukankan      html  css  js  c++  java
  • 莫队算法入门(暴力而不失优雅)

    初学者建议观看:

    传送门

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

    块大小优化

    好吧,在写这个之前,我从机房巨佬空中得到了一个结论莫队的复杂度是
    (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
    ",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
    ",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
    ",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
    ", res[i]);
        }
    }
  • 相关阅读:
    [转]游戏开发指南
    [转]C++接口定义及实现举例
    [转]关于模板函数/模板类编译成DLL
    [转]游戏程序员要求
    [转]如何定位Release程序崩溃原因
    [转]对0基础MFC者的一点建议
    [转]链接警告 LNK4098
    动态调用WCF
    动态添加删除WCF服务类包
    将Xaml文档转成XPS文档[转]
  • 原文地址:https://www.cnblogs.com/lipu123/p/14077103.html
Copyright © 2011-2022 走看看