首页 > 编程语言 >【算法】莫队

【算法】莫队

时间:2023-09-25 19:44:51浏览次数:40  
标签:int 询问 算法 端点 区间 莫队 size

一、概念

莫队是一种应用于离线询问的优美暴力算法。它是主要思想是让区间的左端点和右端点移动的距离加起来最短。

二、实现

假设现在有这样一串序列:\(1,1,4,5,1,4\),我们现在要求询问区间内的 \(1\) 的出现次数。
如果我们现在已经统计到了区间 \((2,3)\),现在询问 \((1,5)\)。
现在的答案是这样的:
image
我们发现现在的左端点 \(x=2\) 在询问的左端点的右边,所以我们将当前左端点向左移一位。现在维护的区间 \([1,3]\) 有两个 \(1\) 了。
image
我们又发现现在的右端点 \(y=3\) 在询问的右端点左边,所以我们将当前右端点向右移一位。现在维护的区间 \([1,4]\) 还是有两个 \(1\)。
image
这时候的右端点还是在询问的右端点。所以我们再将右端点向右移一位。现在维护的区间 \([1,5]\) 有三个 \(1\) 了。
image
这时候访问到的区间与询问的区间完全一样,我们就可以存储答案了。

这就是莫队的思想了(。你会发现这十分的像暴力,的确如此。但是为了保证时间复杂度,我们要引入分块的思想。先将 \(l\) 按照所在块的编号排序,在每个块中,再按 \(r\) 的大小排序。

这样的话我们发现在每个块中,\(r\) 最多会移 \(n\) 次,每换一次块,就可能多移 \(n\) 次。同时,每次询问 \(l\) 都可能移动 \(size\) 次。所以时间复杂度应该为 \(O(n\times num+m\times size)=O(\frac{n^2}{size}+m\times size)\)。

我们发现每换一次块,\(r\) 可能会从最右边跑到最左边,这显然不优。所以我们考虑奇偶性优化,如果第一个块的编号为偶数,那么块的编号为偶数就将 \(r\) 升序排序,为奇数就将 \(r\) 降序排序。如果第一个块的编号为奇数,那么就都反过来。

三、代码

感觉比较板的题是这题:小B的询问

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m,k;
int a[N];
int buc[N]; 
int ansi[N];
int T;
int sum;

struct node{
    int l,r,id;
}q[N];

bool cmp(node x,node y){
    if(x.l/T==y.l/T) return x.r<y.r;
    return x.l<y.l;
}

void add(int x){
    sum-=buc[a[x]]*buc[a[x]];
    buc[a[x]]++;
    sum+=buc[a[x]]*buc[a[x]]; 
}

void del(int x){
    sum-=buc[a[x]]*buc[a[x]];
    buc[a[x]]--;
    sum+=buc[a[x]]*buc[a[x]];
}

signed main(){
    ios::sync_with_stdio(false);
    cin>>n>>m>>k;
    T=sqrt(n);
    for(int i=1;i<=n;++i) cin>>a[i];
    for(int i=1;i<=m;++i){
        cin>>q[i].l>>q[i].r;
        q[i].id=i;
    }
   sort(q+1,q+m+1,cmp);

    int x=1,y=0;
   for(int i=1;i<=m;++i){
       int qx=q[i].l,qy=q[i].r;
       while(x>qx){
           x--;
           add(x);
       }
       while(y<qy){
           y++;
           add(y);
       }
       while(x<qx){
           del(x);
           x++;
       }
       while(y>qy){
           del(y);
           y--;
       }
       ansi[q[i].id]=sum;
    }
    for(int i=1;i<=m;++i) cout<<ansi[i]<<"\n";
    return 0;
}

四、例题

等我咕一下(

标签:int,询问,算法,端点,区间,莫队,size
From: https://www.cnblogs.com/Arcka/p/17728708.html

相关文章

  • 文心一言 VS 讯飞星火 VS chatgpt (100)-- 算法导论9.3 6题
    六、用go语言,对一个包含n个元素的集合来说,k分位数是指能把有序集合分成k个等大小集合的第k-1个顺序统计量。给出一个能找出某一集合的k分位数的O(nlgk)时间的算法。文心一言:要找到一个集合的k分位数,可以使用Go语言编写以下算法:packagemainimport("fmt""sort"......
  • [剑指offer] 其他算法[上]篇
    JZ66构建乘积数组/*暴力*/publicclassJZ66_1{publicstaticint[]multiply(int[]A){int[]res=newint[A.length];Arrays.fill(res,1);for(inti=0;i<A.length;i++){for(intj=0;j<A.len......
  • 快速排序/选择算法
    ......
  • 基础双指针算法:单队列、双队列
    1、单队列输入一串字符串,字符串有多个由单个逗号隔开的单词,任务是需要把单词间隔开,每个单词换行输出。输入样例abcdefghi输出样例abcdefghi#include<iostream>usingnamespacestd;constintN=1010;intmain(){charstr[N];#definegets(str)gets_s(str......
  • 9.25算法
    #include<bits/stdc++.h>usingnamespacestd;structListNode{  intval;  ListNode*next;  ListNode():val(0),next(nullptr){}  ListNode(intx):val(x),next(nullptr){}  ListNode(intx,ListNode*next):val(x),next(next){}};......
  • 轻松掌握冒泡排序算法,值得收藏
    冒泡排序(BubbleSort)是一种简单的排序算法,其基本思想是多次遍历待排序的数组,每次比较相邻的两个元素,如果它们的顺序不正确就交换它们,直到整个数组有序为止。冒泡排序的基本步骤如下:从数组的第一个元素开始,比较相邻的两个元素,如果它们的顺序不正确就交换它们。重复步骤1,直到遍历......
  • 本地测试Spark的逻辑回归算法
    本地小数据量测试了一下Spark的LogisticRegressionWithSGD算法,效果不尽如人意。    数据样例如下,竖杠前的0,1代表两种类型,后面逗号隔开的是两个特征,两个特征只要有一个大于等于0.6就会被分为1这一类,否则就是0。1|0.3,0.60|0.2,0.11|0.5,0.61|0.8,0.30|0.4,0.30|0.3,0.......
  • 简单而经典:Java中的冒泡排序算法详解
    当谈到简单的排序算法时,冒泡排序(BubbleSort)通常是其中之一。虽然它不是最高效的排序算法之一,但它的简单性和易于理解使它成为学习排序算法的良好起点。在本文中,我们将详细介绍Java中的冒泡排序。冒泡排序的基本原理冒泡排序(BubbleSort)是一种简单的排序算法,它通过多次遍历待排序的......
  • 【算法】归并排序算法
    归并排序归并排序的思想归并排序运用了典型的分治策略,是一种稳定的排序算法,其时间复杂度为\(O(nlogn)\),空间复杂度为\(O(n)\)。分治的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。......
  • R语言使用Metropolis-Hastings采样算法自适应贝叶斯估计与可视化|附代码数据
    原文链接:http://tecdat.cn/?p=19889原文出处:拓端数据部落公众号 最近我们被客户要求撰写关于Metropolis-Hastings采样的研究报告,包括一些图形和统计输出。如果您可以写出模型的似然函数,则 Metropolis-Hastings算法可以负责其余部分(即MCMC)。我写了r代码来简化对任意模型的后......