zoukankan      html  css  js  c++  java
  • 莫队算法学习记录

    https://vjudge.net/group/guati可以去这里找我挂的题,第二第三道要硬拿莫队做,有点难,不建议写。
    以下为学习记录:
    http://codeforces.com/contest/617/problem/E

    昨天补了一道非常接近莫队思维的题
    可以看看我这篇博客E题然后再来看莫队就好理解了:
    https://blog.csdn.net/weixin_43262291/article/details/90271693
    (之前贴错链接了。。。已更)

    1. 普通莫队
      莫队其实就是一个暴力的算法,它适用于询问m个区间查询,对于每一个查询没有强制在线并且每个l-r可以O1的转换到l-1-r,l+1-r,l-r-1,l-r+1。

    例题:cf 617E:http://codeforces.com/contest/617/problem/E
    题解:
    A. n的1e5和m的1e5通过莫队来实现
    B. 前缀异或
    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long 
    #define forn(i,n) for(int i=0;i<n;i++)
    #define for1(i,n) for(int i=1;i<=n;i++)
    #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    const int maxn = 1e5+5;
    
    int a[maxn],pos[maxn],cnt[1<<21];
    ll ans[maxn];
    
    struct node{
        int l,r,id;
    }q[maxn];
    
    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(){
        IO;
        int n,m,k;cin>>n>>m>>k;
        int z = sqrt(n);
        for1(i,n){
            cin>>a[i];
            a[i]^=a[i-1];
            pos[i] = i/z;
        }
        forn(i,m){
            cin>>q[i].l>>q[i].r;
            q[i].id = i;
        }
        sort(q,q+m,cmp);
        int l=1,r = 0;
        cnt[0] = 1;
        ll res = 0;
        forn(i,m){
            while(r<q[i].r){
                r++;
                res+=cnt[a[r]^k];
                cnt[a[r]]++;
            }
            while(l>q[i].l){
                l--;
                res+=cnt[a[l-1]^k];
                cnt[a[l-1]]++;
            }
            while(r>q[i].r){
                cnt[a[r]]--;
                res-=cnt[a[r]^k];
                r--;
            }
            while(l<q[i].l){
                cnt[a[l-1]]--;
                res-=cnt[a[l-1]^k];
                l++;
            }
            ans[q[i].id] = res;
        }
        forn(i,m) cout <<ans[i]<<'
    ';
        return 0;
    }
    
    1. 2019CCPC湘潭站 C题 HDU-6534
      莫队+树状数组(这题卡常–线段树会T)
      https://cn.vjudge.net/problem/HDU-6534
      很容易看出来是莫队题。
      题目求l,r内相减绝对值小于K,那么我们可以离散化每个点 v,v+k,v-k然后用树状数组维护出现的次数
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define forn(i,n) for(int i=0;i<n;i++)
    #define for1(i,n) for(int i=1;i<=n;i++)
    #define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    const int maxn = 27005;
    int n,m,k,z;
    struct A{
        int l,r,v;
    }a[maxn];
    vector<int>b;
    struct q{
        int l,r,id;
    }Q[maxn];
    ll ans[maxn];
    bool cmp(q a,q b){
        if(a.l/z==b.l/z) return a.r<b.r;
        return a.l<b.l;
    }
    struct bit{
        int node[maxn<<2];
        void init(){forn(i,maxn<<2)node[i] = 0;};
        inline int lb(int x){return x&(-x);};
        void update(int pos,int v){for(int i=pos;i<=b.size();i+=lb(i))node[i]+=v;};
        int ask(int pos){int sum = 0;for(int i= pos;i;i-=lb(i))sum+=node[i];return sum;};
        inline int query(int l,int r){return ask(r)-ask(l-1);};
    }BIT;
    
    void Hash(){
        sort(b.begin(),b.end());
        b.erase(unique(b.begin(),b.end()),b.end());
        for1(i,n){
            a[i].l = lower_bound(b.begin(),b.end(),a[i].l)-b.begin()+1;
            a[i].v = lower_bound(b.begin(),b.end(),a[i].v)-b.begin()+1;
            a[i].r = lower_bound(b.begin(),b.end(),a[i].r)-b.begin()+1;
        }
    }
    
    int main(){
        IO;
        cin>>n>>m>>k;
        BIT.init();
        for1(i,n){
            cin>>a[i].v;
            a[i].l = a[i].v-k,a[i].r = a[i].v+k;
            b.push_back(a[i].v),b.push_back(a[i].l),b.push_back(a[i].r);
        }
        z = sqrt(n);
        forn(i,m){
            cin>>Q[i].l>>Q[i].r;
            Q[i].id = i;
        }
        sort(Q,Q+m,cmp);
        Hash();
        ll res = 0;
        int l = 1,r = 0;
        forn(i,m){
            while(r<Q[i].r) r++,res+=BIT.query(a[r].l,a[r].r),BIT.update(a[r].v,1);
            while(r>Q[i].r) BIT.update(a[r].v,-1),res-=BIT.query(a[r].l,a[r].r),r--;
            while(l<Q[i].l) BIT.update(a[l].v,-1),res-=BIT.query(a[l].l,a[l].r),l++;
            while(l>Q[i].l) l--,res+=BIT.query(a[l].l,a[l].r),BIT.update(a[l].v,1);
            ans[Q[i].id] = res;
        }
        forn(i,m) cout<<ans[i]<<'
    ';
        return 0;
    }
    

    回滚莫队bzoj4241
    回滚的话好像只有一篇题解讲得比较好,大致意思是指,莫队排完序之后,在同一块内,右指针是排好序的,当遇到一种情况,我们只能添加不能删除比如维护最大值,就得利用这个性质,每次右指针移动,记录状态,然后左指针移动记录答案然后再回到上次的状态即可,这个复杂度还是n根号n的,如果在同一块中就暴力根号n即可

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define forn(i,n) for(int i = 0;i<n;i++)
    #define for1(i,n) for(int i=1;i<=n;i++)
    #define IO ios::sync_with_stdio(false);cin.tie(0)
    const int maxn = 1e5+5;
    vector<int>b;
    int a[maxn],num[maxn],num2[maxn],pos[maxn],has[maxn],n,m;
    ll ans[maxn];
    struct Q{
    	int l,r,id;
    }q[maxn];
    bool cmp(Q a,Q b){
    	if(pos[a.l]==pos[b.l]) return a.r<b.r;
    	return pos[a.l]<pos[b.l];
    }
    void ha(){
    	sort(b.begin(),b.end());
    	b.erase(unique(b.begin(),b.end()),b.end());
    	for1(i,n){
    		int p = lower_bound(b.begin(),b.end(),a[i])-b.begin();
    		has[i] = p;
    	}
    }
    
    int main(){
    	IO;
        cin>>n>>m;
    	int z = sqrt(n);
    	for1(i,n) {
    		cin>>a[i];
    		b.push_back(a[i]);
    		pos[i] = (i-1)/z;
    	}
    	forn(i,m){
    		cin>>q[i].l>>q[i].r;
    		q[i].id = i;
    	}
    	ha();
    	sort(q,q+m,cmp);
    	ll res = 0,res2 = 0;
        bool ok = 1;
    	int l = (pos[q[0].l]+1)*z,r =l-1,l2 = l; 
    	forn(i,m){
    		if(pos[q[i].l]!=pos[q[i-1].l]){
                res = 0,res2 = 0;
                l2 = l = (pos[q[i].l]+1)*z,r = l-1;
                if(!ok) {
                    memset(num2,0,sizeof(num2));
                    ok = 1;
                }
            }if(pos[q[i].l]==pos[q[i].r]){
                res = 0;
                for(int j = q[i].l;j<=q[i].r;j++){
                    num[has[j]]++;
                    res = max(res,1ll*a[j]*num[has[j]]);
                }
                for(int j = q[i].l;j<=q[i].r;j++){
                    num[has[j]]--;
                }
            }else{
                ok = 0;
                while(r<q[i].r) r++,num2[has[r]]++,res2 = max(res2,1ll*a[r]*num2[has[r]]);
                l = l2,res = res2;
                while(l>q[i].l) l--,num2[has[l]]++,res = max(res,1ll*a[l]*num2[has[l]]);
                while(l<l2) num2[has[l]]--,l++;
            }
            ans[q[i].id] = res;
    	}
    	forn(i,m) cout<<ans[i]<<'
    ';
    	return 0;
    }
    

    带修改莫队,貌似分块n^2/3更快。
    bzoj2120

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define forn(i,n) for(int i = 0;i<n;i++)
    #define for1(i,n) for(int i =1;i<=n;i++)
    #define IO ios::sync_with_stdio(false);cin.tie(0)
    const int maxn = 1e5+5;
    const int maxm = 1e6+5;
    int a[maxn],num[maxm],pos[maxn],vis[maxn],ans[maxn],pre[maxn],res;
    struct Q{
        int l,r,id,tim;
    }q[maxn];
    struct C{
        int p,v,pre;
    }c[1005];
    bool cmp(Q a,Q b){
        if(pos[a.l]==pos[b.l]){
            if(a.r==b.r) return a.tim<b.tim;
            return a.r<b.r;
        }
        return pos[a.l]<pos[b.l];
    }
    void cal(int p){
        if(vis[p]){
            num[a[p]]--;
            if(!num[a[p]]) res--;
        }else{
            if(!num[a[p]]) res++;
            num[a[p]]++;
        }
        vis[p]^=1;
    }
    void change(int p,int v){
        if(vis[p]){
            cal(p);
            a[p] = v;
            cal(p);
        }else a[p] = v;
    }
     
    int main(){
        IO;
        int n,m;cin>>n>>m;
        int z = sqrt(n);
        for1(i,n){
            cin>>a[i];
            pre[i] = a[i];
            pos[i] = i/z;
        }
        int cntq = 0,cntc = 0;
        forn(i,m){
            char op;int x,y;cin>>op>>x>>y;
            if(op=='Q'){
                q[cntq] = {x,y,cntq,cntc};
                cntq++;
            }else {
                c[++cntc] = {x,y,pre[x]};
                pre[x] = y;
            }
        }
        sort(q,q+cntq,cmp);
        int l = 1,r = 0;
        for(int j = 1;j<=q[0].tim;j++) change(c[j].p,c[j].v);
        forn(i,cntq){
            if(i)for(int j = q[i-1].tim+1;j<=q[i].tim;j++) change(c[j].p,c[j].v);
            if(i)for(int j = q[i-1].tim;j>q[i].tim;j--) change(c[j].p,c[j].pre);
            while(r<q[i].r) cal(++r);
            while(l>q[i].l) cal(--l);
            while(r>q[i].r) cal(r--);
            while(l<q[i].l) cal(l++);
            ans[q[i].id] = res;
        }
        forn(i,cntq) cout<<ans[i]<<'
    ';
        return 0;
    }
    
    人一我百,人十我万。
  • 相关阅读:
    Jenkins动态部署方案
    01-Java学习笔记
    Tcp实现简单的大小写转换功能
    JavaScript 执行环境 与 变量对象
    JQuery
    JavaScript模板引擎
    ECMA6
    谷歌控制台
    prototype 与 __proto__
    JavaScript Math
  • 原文地址:https://www.cnblogs.com/AlexPanda/p/12520333.html
Copyright © 2011-2022 走看看