莫队算法 ( MO's algorithm )

莫队算法是由清华大学神牛莫涛发明的一种处理区间问题的离线算法

算法核心是通过先将问询区间总长度平方分块、然后将所有的问询区间按照左端点所在的块编号排序、在同一块内的则按右端点升序

然后设置左右两个下标指针、每次都移动两个指针指向问询块的左右端点、在移动的过程中不断维护答案。

可以证明原本只通过两个下标指针移动来处理问询的方法最坏可达 O(N*Q) 经过莫队算法排序后可降为 O((N+Q)*sqrt(N))

所以莫队算法其实就是排个序

当然经过我粗略的概述肯定是无法讲清楚的,这里给出几个链接方便参考和学习此算法

莫队算法还是莫队算法当然还是莫队算法

 

一些题目

BZOJ Mato的文件管理

分析 :

对于每个区间、实际就是查询区间逆序对的个数。

看到数据范围和不强制在线考虑使用莫队算法解决、先确定分块长度然后对所有问询进行离线排序、关键在于怎么更新。

更新算法和指针左右移动密切相关,指针的移动可以看成从 左/右 添加或者删除一个数,那么这就很好做了

在左边添加一个数、比这个数小的都贡献了一个逆序对、加上

在右边添加一个数、比这个数大的都贡献了一个逆序对、加上

在左边删除一个数、原本比这个数小的都贡献了一个逆序对、减去

在右边删除一个数、原本比这个数大的都贡献了一个逆序对、减去

注意一下左右指针移动的时候是先指针移动再更新还是先更新再移动

#include<bits/stdc++.h>
#define lowbit(i) (i & (-i))
using namespace std;
const int maxn = 1e5 + 10;
struct QUERY{
    int L, R, Len, id;
    bool operator < (const QUERY &rhs) const{
        if((L/Len) == (rhs.L/rhs.Len)) return R < rhs.R;
        else return (L/Len) < (rhs.L/rhs.Len);
    };
}Q[maxn]; int ans[maxn];

int arr[maxn], N;
int uni[maxn], uniLen;

int Bit[maxn];

inline void BitAdd(int i, int val)
{
    while(i <= N){
        Bit[i] += val;
        i += lowbit(i);
    }
}

int BitSum(int i)
{
    if(i == 0) return 0;
    int ret = 0;
    while(i > 0){
        ret += Bit[i];
        i -= lowbit(i);
    }return ret;
}

int GetVal(int i)
{
    int ret = lower_bound(uni, uni+uniLen, arr[i]) - uni;
    return ++ret;
}

int main(void)
{
    scanf("%d", &N);
    for(int i=0; i<N; i++) scanf("%d", &arr[i]), uni[i] = arr[i];

    sort(uni, uni+N);
    ///题目貌似没说每个元素的大小,干脆离散化好了
    uniLen = unique(uni, uni+N) - uni;

    int qNum, sqrt_N = (int)sqrt(N);
    scanf("%d", &qNum);
    for(int i=0; i<qNum; i++){
        scanf("%d %d", &Q[i].L, &Q[i].R);
        Q[i].Len = sqrt_N;
        Q[i].id = i;
    }

    sort(Q, Q+qNum);

    int val, curL, curR, CurAns = 0;
    curL = 1, curR = 0;
    for(int i=0; i<qNum; i++){
        ///在左边添加一个数、比这个数小的都贡献了一个逆序对、加上
        while(curL > Q[i].L){
            curL--;
            val = GetVal(curL-1);
            BitAdd(val, 1);
            CurAns += BitSum(val-1);
        }

        ///在右边添加一个数、比这个数大的都贡献了一个逆序对、加上
        while(curR < Q[i].R){
            curR++;
            val = GetVal(curR-1);
            BitAdd(val, 1);
            CurAns += curR - curL - BitSum(val-1);
        }

        ///在左边删除一个数、原本比这个数小的都贡献了一个逆序对、减去
        while(curL < Q[i].L){
            val = GetVal(curL-1);
            BitAdd(val, -1);
            CurAns -= BitSum(val-1);
            curL++;
        }

        ///在右边删除一个数、原本比这个数大的都贡献了一个逆序对、减去
        while(curR > Q[i].R){
            val = GetVal(curR-1);
            BitAdd(val, -1);
            CurAns -= curR - curL - BitSum(val-1);
            curR--;
        }

        ans[Q[i].id] = CurAns;
    }

    for(int i=0; i<qNum; i++) printf("%d\n", ans[i]);
    return 0;
}
View Code

 

posted @ 2018-06-05 00:02  qwerity  阅读(482)  评论(0编辑  收藏  举报