莫队算法学习记录

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;
}
人一我百,人十我万。
原文地址:https://www.cnblogs.com/AlexPanda/p/12520333.html