莫队算法

【前言】

莫队算法(Mo’s algorithm)是由莫涛队长发明的,一种处理区间问题的离线算法,由于其代码简便,常数巨小,适合各种打暴力。

另:关于带修改的莫队,可以看这里

【做法】

前提:如果已知区间[L,R]的答案,可以很快(log或常数级别)地得到区间[L±1,R±1]的答案。
对于区间询问[L,R],我们可以先读进来所有询问,按照某种顺序依次处理(线性移动L,R端点),就可以得到所有询问的答案。

那么,处理各个询问的顺序就十分重要了。
我们可以利用分块的思想,把1~n分成√n块,对于L在同一块的询问,按R排序。
下面证明复杂度:
对于L,最坏是每次走√n,即O(Q√n)
对于R,有√n块,每个块最坏走n次,即O(n√n)
所以总复杂度是O(n√n)

【例题】

BZOJ 2038

#include<cstdio>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const int maxn=50005;
int n,q,a[maxn],h[maxn],hsh[maxn];
LL now;
LL c2(int x) {return (LL)x*(x-1)/2;}
LL gcd(LL x,LL y){
    if (y==0) return x;
    return gcd(y,x%y);
}
struct data{
    int l,r,id;
    bool operator<(const data&b)const{
        if (h[l]==h[b.l]) return r<b.r;
        return l<b.l;
    }
}que[maxn];
struct la{
    LL x,y;
}ans[maxn];
inline int red(){
    int tot=0,f=1;char ch=getchar();
    while (ch<'0'||'9'<ch) {if (ch=='-') f=-f;ch=getchar();}
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    return f*tot;
}
void blocker(){
    int k=sqrt(n);
    for (int i=1;i<=n;i++) h[i]=(i-1)/k+1;
}
void update(int x,int d){
    now-=c2(hsh[a[x]]);
    hsh[a[x]]+=d;
    now+=c2(hsh[a[x]]);
}
int main(){
    n=red(),q=red();
    for (int i=1;i<=n;i++) a[i]=red();
    blocker();
    for (int i=1;i<=q;i++) que[i].l=red(),que[i].r=red(),que[i].id=i;
    sort(que+1,que+1+q);
    update(1,1);
    for (int i=1,L=1,R=1;i<=q;i++){
        while (L<que[i].l) update(L++,-1);
        while (L>que[i].l) update(--L,1);
        while (R<que[i].r) update(++R,1);
        while (R>que[i].r) update(R--,-1);
        ans[que[i].id]=(la){now,c2(que[i].r-que[i].l+1)};
    }
    for (int i=1;i<=q;i++){
        LL t=gcd(ans[i].x,ans[i].y);
        ans[i].x/=t;ans[i].y/=t;
        if (ans[i].x==0) printf("0/1\n");else printf("%lld/%lld\n",ans[i].x,ans[i].y);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
莫队算法是一种基于分块的算法,用于解决一些静态区间查询问题,时间复杂度为 $O(n\sqrt{n})$。以下是一个基于Python的莫队算法的示例代码: ```python import math # 定义块的大小 BLOCK_SIZE = 0 # 初始化块的大小 def init_block_size(n): global BLOCK_SIZE BLOCK_SIZE = int(math.sqrt(n)) # 定义查询操作 def query(left, right): pass # 在这里写查询操作的代码 # 定义添加操作 def add(x): pass # 在这里写添加操作的代码 # 定义删除操作 def remove(x): pass # 在这里写删除操作的代码 # 定义莫队算法 def mo_algorithm(n, q, queries): init_block_size(n) queries.sort(key=lambda x: (x[0] // BLOCK_SIZE, x[1])) left, right = 0, -1 for query in queries: while left > query[0]: left -= 1 add(left) while right < query[1]: right += 1 add(right) while left < query[0]: remove(left) left += 1 while right > query[1]: remove(right) right -= 1 query(query[0], query[1]) ``` 在这段代码中,我们首先定义了一个全局变量 `BLOCK_SIZE`,用于表示块的大小。接着,我们定义了三个操作函数 `query()`、`add()` 和 `remove()`,分别用于查询、添加和删除元素。在 `mo_algorithm()` 函数中,我们首先调用 `init_block_size()` 函数初始化块的大小,然后将查询操作按照块的大小和右端点排序,接着使用双指针维护当前查询区间的左右端点,每次移动指针时调用 `add()` 和 `remove()` 函数更新块的状态,最后调用 `query()` 函数进行查询操作。 请注意,这段代码只是一个示例,具体的 `query()`、`add()` 和 `remove()` 函数的实现取决于具体的问题。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值