莫队算法小结

神奇的莫队算法!!!我只能说Qrz,之前一直在一道题上面傻逼了,wa到死,附上链接:http://www.spoj.com/problems/ZQUERY/en/ 个人觉得还是一道蛮不错的题。

莫队简单粗暴,据说可以再o(nlogn)内解决一切无修改的区间查询问题!!!核心应该就是:1:按左端点分块排序。2:能在o(1)的复杂度内解决[L,R] -> [L,R+1],[L,R]->[L,R-1],[L,R] -> [L-1,R],[L,R] -> [L+1,R]所维护的值,当着o(1)视情况而定。只要保证复杂度满足题目条件即可。

莫队另外一个优点就是易于实现!!!好写!!!再次Orz莫涛大神!!!

下面介绍几道入门题:

1:小Z的袜子:http://www.lydsy.com/JudgeOnline/problem.php?id=2038

最基本的题,直接给代码了:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define N 50010

using namespace std;

const int M = (int)sqrt(1.0*N)+1;
int n,m;
int cnt[N],col[N];

struct Command{
    ll l,r,id;
    bool operator < (const Command& rhs) const{
        if(l/M == rhs.l/M)  return r < rhs.r;
        return (l/M) < (rhs.l/M);
    }
}cmd[N];

struct Ans{
    ll mol,den;
}ans[N];

ll gcd(ll a,ll b){
    return b == 0 ? a : gcd(b,a%b);
}

void solve(){
    ll L = 1,R = 0;
    ll temp = 0;
    FOR(i,0,m){
        while(R < cmd[i].r) {
            R ++;
            temp -= cnt[col[R]]*cnt[col[R]];
            cnt[col[R]] ++;
            temp += cnt[col[R]]*cnt[col[R]];
        }
        while(R > cmd[i].r){
            temp -= cnt[col[R]]*cnt[col[R]];
            cnt[col[R]] --;
            temp += cnt[col[R]]*cnt[col[R]];
            R --;
        }
        while(L < cmd[i].l){
            temp -= cnt[col[L]]*cnt[col[L]];
            cnt[col[L]] --;
            temp += cnt[col[L]]*cnt[col[L]];
            L ++;
        }
        while(L > cmd[i].l){
            L --;
            temp -= cnt[col[L]]*cnt[col[L]];
            cnt[col[L]] ++;
            temp += cnt[col[L]]*cnt[col[L]];
        }
        ll x = temp - (R-L+1);
        ll y = (R-L+1)*(R-L);
        if(!x)   ans[cmd[i].id].mol = 0,ans[cmd[i].id].den = 1;
        else{
            ll tem = gcd(x,y);
            ans[cmd[i].id].mol = x/tem;
            ans[cmd[i].id].den = y/tem;
        }
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        memset(cnt,0,sizeof(cnt));
        FOR(i,1,n+1)    scanf("%d",&col[i]);
        FOR(i,0,m){
            scanf("%lld%lld",&cmd[i].l,&cmd[i].r);
            cmd[i].id = i;
        }
        sort(cmd,cmd+m);
        solve();
        FOR(i,0,m){
            printf("%lld/%lld\n",ans[i].mol,ans[i].den);
        }
    }
    return 0;
}
2:Sona https://ac.2333.moe/Problem/view.xhtml?id=1457 

这道题给人一种然并卵的感觉,和上题基本一样

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <map>
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define ll long long
#define N 100100

using namespace std;

const int M = (int)sqrt(1.0*N);
int n,m;
ll cnt[N],col[N];
ll ans[N];

struct Col{
    ll note;
    int id;
    bool operator < (const Col& rhs) const{
        return note < rhs.note;
    }
}con[N];

struct Commend{
    int l,r;
    int id;
    bool operator < (const Commend& rhs)  const{
        if(l/M == rhs.l/M)  return r < rhs.r;
        return (l/M) < (rhs.l/M);
    }
}cmd[N];

void solve(){
    int L = 1,R = 0;
    FOR(i,0,N)  cnt[i] = 0;
    ll temp = 0;
    FOR(i,0,m){
        while(R < cmd[i].r){
            R ++;
            temp -= cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
            cnt[col[R]] ++;
            temp += cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
        }
        while(R > cmd[i].r){
            temp -= cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
            cnt[col[R]] --;
            temp += cnt[col[R]]*cnt[col[R]]*cnt[col[R]];
            R --;
        }
        while(L < cmd[i].l){
            temp -= cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
            cnt[col[L]] --;
            temp += cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
            L ++;
        }
        while(L > cmd[i].l){
            L --;
            temp -= cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
            cnt[col[L]] ++;
            temp += cnt[col[L]]*cnt[col[L]]*cnt[col[L]];
        }
        ans[cmd[i].id] = temp;
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d",&n)){
        FOR(i,0,n)    {scanf("%I64d",&con[i].note);con[i].id = i+1;}
        sort(con,con+n);
        int cot = 0;
        col[con[0].id] = (++cot);
        FOR(i,1,n){
            if(con[i].note != con[i-1].note)    col[con[i].id] = (++cot);
            else col[con[i].id] = cot;
        }
        scanf("%d",&m);
        FOR(i,0,m){
            scanf("%d%d",&cmd[i].l,&cmd[i].r);
            cmd[i].id = i;
        }
        sort(cmd,cmd+m);
        solve();
        FOR(i,0,m){
            printf("%I64d\n",ans[i]);
        }
    }
    return 0;
}

3:Group http://acm.hdu.edu.cn/showproblem.php?pid=4638 维护一个左边数的位置,右边数的位置,1,n特判一下就行了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define ll long long
#define N 100010
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)

using namespace std;

const int M = (int)sqrt(1.0*N);
int num[N],pos[N],ans[N],l[N],r[N];
int n,m;

struct Commend{
    int l,r;
    int id;
    bool operator < (const Commend& rhs) const{
        if(l/M == rhs.l/M)  return r < rhs.r;
        return (l/M < rhs.l/M);
    }
}cmd[N];

void solve(){
    int L = 1,R = 0;
    int temp = 0;
    FOR(i,0,m){
        while(R < cmd[i].r){
            R ++;
            if(l[num[R]] >= L && r[num[R]] <= R) temp --;
            else if((l[num[R]] < L && r[num[R]] > R) || (l[num[R]] > R) || (r[num[R]] < L))   temp ++;
        }
        while(R > cmd[i].r){
            if(l[num[R]] >= L && r[num[R]] <= R)  temp ++;
            else if((l[num[R]] < L && r[num[R]] > R) || (l[num[R]] > R) || (r[num[R]] < L))   temp --;
            R --;
        }
        while(L < cmd[i].l){
            if(l[num[L]] >= L && r[num[L]] <= R)  temp ++;
            else if((l[num[L]] < L && r[num[L]] > R)  || (l[num[L]] > R) || (r[num[L]] < L))  temp --;
            L ++;
        }
        while(L > cmd[i].l){
            L --;
            if(l[num[L]] >= L && r[num[L]] <= R) temp --;
            else if((l[num[L]] < L && r[num[L]] > R)  || (l[num[L]] > R) || (r[num[L]] < L))  temp ++;
        }
        ans[cmd[i].id] = temp;
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        FOR(i,1,n+1){
            scanf("%d",&num[i]);
            pos[num[i]] = i;
        }
        l[1] = 0;
        r[1] = pos[2];
        r[n] = n+1;
        l[n] = pos[n-1];
        FOR(i,2,n){
           l[i] = min(pos[i-1],pos[i+1]);
           r[i] = max(pos[i-1],pos[i+1]);
        }
        FOR(i,0,m){
            scanf("%d%d",&cmd[i].l,&cmd[i].r);
            cmd[i].id = i;
        }
        sort(cmd,cmd+n);
        solve();
        FOR(i,0,m){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

4:Zero Query http://www.spoj.com/problems/ZQUERY/en/ 我wa到死的一题。这个题要维护一个左边最靠近的一个位置,右边靠近的一个位置,就行了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#define FOR(i,x,y)  for(int i = x;i < y;i ++)
#define IFOR(i,x,y) for(int i = x;i > y;i --)
#define lrt rt<<1
#define rrt rt<<1|1
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll long long
#define N 50050

using namespace std;

int M;
int cnt,col[N],sum[N],n,m,l[N],r[N],st,pr[N],pl[N],ans[N],pos[N];
int maxn,minn;

struct Commend{
    int l,r;
    int id;
    bool operator < (const Commend& rhs) const{
        if(l/M == rhs.l/M)  return r < rhs.r;
        return (l/M) < (rhs.l/M);
    }
}cmd[N];

struct Tree{
    int l,r;
    int maxx;
}tree[N<<2];

void Build(int rt,int l,int r){
    tree[rt].l = l; tree[rt].r = r; tree[rt].maxx = 0;
    if(l == r) return;
    int mid = (l+r)>>1;
    Build(lson);
    Build(rson);
}

void PushUp(int rt) {tree[rt].maxx = max(tree[lrt].maxx,tree[rrt].maxx);}

void Modify(int rt,int k,int val){
    if(tree[rt].l == tree[rt].r)    {tree[rt].maxx = val;return;}
    int mid = (tree[rt].l + tree[rt].r)>>1;
    if(k <= mid)    Modify(lrt,k,val);
    else Modify(rrt,k,val);
    PushUp(rt);
}

void solve(){
    int L = 1,R = 0;
    memset(r,-1,sizeof(r));
    memset(l,-1,sizeof(l));
    FOR(i,0,m){
        while(R < cmd[i].r){
            R ++;
            if(l[col[R]] == -1) l[col[R]] = R;
            r[col[R]] = R;
            Modify(1,col[R],r[col[R]]-l[col[R]]);
        }
        while(R > cmd[i].r){
            r[col[R]] = pr[R];
            if(r[col[R]] < L)   l[col[R]] = r[col[R]] = -1;
            Modify(1,col[R],r[col[R]]-l[col[R]]);
            R --;
        }
        while(L < cmd[i].l){
            l[col[L]] = pl[L];
            if(l[col[L]] > R || l[col[L]] == -1)   l[col[L]] = r[col[L]] = -1;
            Modify(1,col[L],r[col[L]]-l[col[L]]);
            L ++;
        }
        while(L > cmd[i].l){
            L --;
            if(r[col[L]] == -1) r[col[L]] = L;
            l[col[L]] = L;
            Modify(1,col[L],r[col[L]]-l[col[L]]);
        }
        ans[cmd[i].id] = tree[1].maxx;
    }
}

int main()
{
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)){
        M = (int)sqrt(1.0*n);
        int tem;
        sum[1] = col[1] = 0;
        maxn = minn = 0;
        FOR(i,2,n+2){
            scanf("%d",&tem);
            sum[i] = sum[i-1] + tem;
            if(tem == 1) maxn = max(maxn,sum[i]);
            else minn = min(minn,sum[i]);
        }
        cnt = 1-minn;
        FOR(i,1,n+2){
            col[i] = sum[i] + cnt;
        }
        cnt = maxn-minn+1;
        Build(1,1,cnt);
        FOR(i,0,m){
            scanf("%d%d",&cmd[i].l,&cmd[i].r);
            cmd[i].r ++;
            cmd[i].id = i;
        }
        sort(cmd,cmd+m);
        FOR(i,1,cnt+1)  pos[i] = -1;
        FOR(i,1,n+2){
            pr[i] = pos[col[i]];
            pos[col[i]] = i;
        }
        FOR(i,1,cnt+1)  pos[i] = -1;
        IFOR(i,n+1,0){
            pl[i] = pos[col[i]];
            pos[col[i]] = i;
        }
        solve();
        FOR(i,0,m){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}
5:The sum of gcd http://acm.hdu.edu.cn/showproblem.php?pid=5381 和上一题有点像,稍微复杂一点,可以看我这道题写的题解

http://blog.csdn.net/u014610830/article/details/47670615

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值