莫队+分块 【2009国家集训队】小Z的袜子(hose) bzoj2038

本文介绍了一种高效查询处理算法——莫队算法。该算法通过巧妙地利用数据分块和排序技巧,将时间复杂度降低至O(nsqrt(n))。文章详细解释了其工作原理,并提供了一个实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

莫队算法,听起来就很高端的样子啊,但其实还挺好理解的。
就是如果你查询的东西满足你已知[l,r]区间的答案,就能在很快的时间内知道[l+1,r],[l-1,r],[l,r+1],[l,r-1]这四个区间的答案,就可以用莫队搞。

按照l第一关键字,r第二关键字排序,把所有的询问扫一遍,l是递增的,然后把r每次调到它该在的位置,统计答案,这样做时间复杂度上界是n^2的,有点接受无能啊……

所以就可以用我怎么想也想不到的但是神犇们就是能想到的分块了。

把区间分块,然后l在一个块内的询问按照r排序,然后每次处理一个块内的所有询问。
这样可以保证没次询问l的修改范围不超过sqrt(n),总共有m个询问,每个块内对于r的所有修改不超过n,一共有sqrt(n)块。
再加上排序的时间mlogm。

所以总的时间复杂度就是O(msqrt(n)+nsqrt(n)+mlogm)
简化一下就是O(nsqrt(n))啦!
over

代码参照了PoPo姐姐写的>_<,挺好懂的。

代码如下:

#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 50050
using namespace std;
struct query{
    int l,r,num;
    bool operator < (const query&) const;
}q[N];
int n,m,block;
int a[N],belong[N];
long long num[N],den[N],cnt[N];
bool query ::  operator < (const query &c) const
{
    return belong[l]<belong[c.l] || (belong[l]==belong[c.l] && r<c.r);
}
long long gcd(long long a,long long b)
{
    if(!b) return a;
    return gcd(b,a%b);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].num=i;
    block=ceil(sqrt(n));
    for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
    sort(q+1,q+1+m);
    long long now=0;
    long long l=1,r=0;
    for(int i=1;i<=m;i++)
    {
        while(r<q[i].r) now+=cnt[a[++r]]++;
        while(r>q[i].r) now-=--cnt[a[r--]];
        while(l<q[i].l) now-=--cnt[a[l++]];
        while(l>q[i].l) now+=cnt[a[--l]]++;
        long long sum=(r-l)*(r-l+1)>>1;
        long long div=gcd(sum,now);
        num[q[i].num]=now/div;
        den[q[i].num]=sum/div;
    }
    for(int i=1;i<=m;i++) printf("%lld/%lld\n",num[i],den[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值