首页 > 其他分享 >莫队

莫队

时间:2024-03-27 12:13:13浏览次数:21  
标签:cnt int 复杂度 pos sqrt ans 莫队

以为自己一辈子接触不到的算法,本来以为很高深,没想到是优雅的暴力,太绝妙了

对于多个区间查询,例如区间最大值等,我们考虑暴力,枚举区间 $[L,R]$,取最大值即可,时间复杂度 $O(m*(R-L))$,跑不起,所以我们借用数据结构,单调队列,树状数组等等,但是如果此时我们考虑优化暴露

首先我们这样优化: 设两个指针,然后如果左指针小于左端点,那么左指针向右走,依次遍历,这样可以减少遍历次数

其次: 考虑左指针,我们可以存取每个区间,然后按照区间的左端点排序,这样左指针最多走 $n$ 次,但是右端点无序,最坏仍然是 $O(n^2)$

那么我们能不能取个平衡呢? 也就是我们使左指针的复杂度升高,右指针的复杂度降低,此时我们考虑分块,一块一块的来求,我们把序列分为 $\sqrt{n}$ 块,然后对查询区间进行排序,如果左端点不在同一个块上,那么按照块大小排序,如果在一个块上,那么按照右端点进行排序
这样我们的左端点可以做到部分有序,右端点有序, 接下来讨论复杂度:

对于左指针:

设每个块 $i$ 中分布有 $x_i$ 个左端点,由于莫队的添加、删除操作复杂度为 $O(1)$,那么处理块 $i$ 的最坏时间复杂度是 $O(x_i\sqrt{n})$,指针跨越整块的时间复杂度为 $O(\sqrt{n})$,最坏需要跨越 $n$ 次;总复杂度 $O(\sum x_i \sqrt{n})=O(n\sqrt{n})$。

对于右指针:

由于在一个块内的右端点是有序的,那么我们遍历一个块时,右指针最坏移动 $n$ 个点,一共有$\sqrt{n}$个块,那么时间复杂度为: $O(n\sqrt{n})$

排序复杂度: $(O(n\log n)\)$

总复杂度: $O(n\sqrt{n})$

模板:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>

using namespace std;

const int N = 1e6 + 10;

int a[N];
int pos[N];
int ans[N];
int res; // 维护当前[l,r]的值 

struct Query {
    int l, r, k;
}q[N];

void add(int pos){

}
void del(int pos){

}
int main () {
    int n, m;
    cin >> n >> m;
    
    int t = sqrt (n);
    for (int i = 1; i <= n; i ++) {    
        cin >> a[i];
        pos[i] = i / t + 1;
    }
    
    for (int i = 1; i <= m; i ++)
        cin >> q[i].l >> q[i].r;
    
    sort (q + 1, q + 1 + m, [](Query x, Query y) {
        return pos[x.l] == pos[y.l] ? x.r < y.r : pos[x.l] < pos[y.l];
    });
    
    int l = 1, r = 0; // 始终维护[l,r]的答案,对于每一次询问都移动l,r指针,使其符合询问的区间 
    
    for (int i = 1; i <= m; i ++) {
        while (q[i].l < l) add (-- l); 
        while (q[i].r > r) add (++ r);
        while (q[i].l > l) del (l ++);
        while (q[i].r < r) del (r --);
        // 记录答案 ....
        ans[q[i].k] = res;
    }
    
    for (int i = 1; i <= m; i ++)
        cout << ans[i] << " "; 
    
    return 0;
} 

相关题:

0重复的数 - 蓝桥云课 (lanqiao.cn)

 

#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
const int N=2e5+10,mod=1e9+7;
int pos[N],a[N],cnt[N],ans[N],res[N];
struct node{
    int l,r,k,now;
    bool operator<(const node&w)const{
        return (pos[l]^pos[w.l])?pos[l]<pos[w.l]:((pos[l]&1)?r<w.r:r>w.r);
    }
}p[N];
void add(int x){
    ans[cnt[a[x]]]--,cnt[a[x]]++,ans[cnt[a[x]]]++;
}
void del(int x){
    ans[cnt[a[x]]]--,cnt[a[x]]--,ans[cnt[a[x]]]++;
}
signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n; cin>>n;
    int len=sqrt(n);
    for(int i=1;i<=n;i++) cin>>a[i],pos[i]=i/len;
    int m; cin>>m;
    for(int i=1;i<=m;i++){
        int a,b,c; cin>>a>>b>>c;
        p[i]={a,b,c,i};
    }
    sort(p+1,p+1+m);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        int num=p[i].k,id=p[i].now;
        while(l<p[i].l) del(l++);
        while(l>p[i].l) add(--l);
        while(r<p[i].r) add(++r);
        while(r>p[i].r) del(r--);
        res[id]=ans[num];
    }
    for(int i=1;i<=m;i++) cout<<res[i]<<endl;
}

P2709 小B的询问 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N=1e6+10,mod=1e9+7;
int g[N],pos[N],a[N],cnt[N],ans;
struct node{
    int l,r,id;
    bool operator<(const node &w)const{
        return (pos[l]^pos[w.l])?pos[l]<pos[w.l]:((pos[l]&1)?r<w.r:r>w.r);
    }
}p[N];
void add(int x){
    cnt[a[x]]++,ans+=2*cnt[a[x]]-1;
}
void del(int x){
    ans-=2*cnt[a[x]]-1,cnt[a[x]]--;
}
signed main(){
    std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n,m,k; cin>>n>>m>>k;
    int len=sqrt(n);
    for(int i=1;i<=n;i++) cin>>a[i],pos[i]=i/len;
    for(int i=1;i<=m;i++){
        int a,b; cin>>a>>b;
        p[i]={a,b,i};
    }
    sort(p+1,p+1+m);
    vector<int>res(m+1);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while(l<p[i].l) del(l++);
        while(l>p[i].l) add(--l);
        while(r<p[i].r) add(++r);
        while(r>p[i].r) del(r--);
        res[p[i].id]=ans;
    }
    for(int i=1;i<=m;i++) cout<<res[i]<<endl;
}

 

标签:cnt,int,复杂度,pos,sqrt,ans,莫队
From: https://www.cnblogs.com/o-Sakurajimamai-o/p/18098664

相关文章

  • 莫队学习笔记
    模板。然后我不会做。然后我去看题解,看莫队学习笔记,看不懂。然后我摆烂了。然后去玩按住shift让光标左右动的无聊游戏。我最开始选中了标红点的部分。多选中了左边的一个点,少选中了右边的一个点。然后我会莫队了?......
  • CF1514D-区间(绝对)众数-莫队、随机化、可持久化线段树
    link:https://codeforces.com/contest/1514/problem/D很久以前小号打的场了,当时D题写的莫队,现在重新来看看。题意:给一个序列\([a_1,\dots,a_n]\),有q次询问,每次问:把\([a_l,\dots,a_r]\)划分最少几个不相交子序列,才能使得每个子序列是beautiful的。称一个序列\(a_1,\dots,a_x\)......
  • 【学习笔记】分块/莫队
    众所周知,分块是一种比较暴力的数据结构。虽说分块效率不高,但它能处理一些树状数组和线段树难以维护的东西(尤其是不具备可拆分性和可合并性的东西)。分块遵循整块维护,块内暴力的原则。所以我们一般先考虑一个暴力算法,再使用分块优化。建立分块:我们定义一个分块的结构体b,分别存......
  • 莫队与分块学习笔记
    分块思想介绍分块是一种思想,而不是一种数据结构。思想就是,将一块大的区间,转换成小的区间来处理。例如,在一个\(n\)长度上的数轴,我们可以将其分成\(\sqrtn\)个长度为\(\sqrtn\)的块来解决。典型问题对于一类很典型的问题,可以用分块来做。单点修改,区间查询这玩意咋......
  • [莫队]
    P2709莫队特点是一种优雅的暴力解决大部分区间离线问题的离线算法主要思想为分块,将\(n^2\)降为\(n\sqrt{n}\)题目关键词包含\(n,m,k\),并有多个询问\(L_i,R_i\),求区间内的...思想相当于有两个指针\(L,R\),若当前询问的区间为\(l[i],r[i]\)那么会分别将\(L,R\)向\(l[i......
  • 分块和莫队
    1分块1.1概念简述分块被称为“优雅的暴力”。对于一个区间$[1,n]$,我们将其分成若干个块。在处理整块时直接维护整块信息,达到降低时间复杂度的目的。对于常规分块,设块长为$m$,则一般情况下$m$取$\sqrt{n}$时复杂度最优。下面举几例来说明分块如何降低时间复杂度。1.2......
  • 根号分治与莫队算法
    1.根号分治与分块在预处理与询问的复杂度之间寻找平衡的一个算法,通常以根号为分界线。属于智慧的暴力。1.1.根号平衡使用数学不等式对于阈值取一个值,使得复杂度最优。如果有阈值\(B\),若问题有一部分暴力可以\(O(B)\)解决,另一部分可以\(O(\frac{n}{B})\)解决。那么根据基......
  • 莫队算法学习笔记
    普通莫队形式¶假设\(n=m\),那么对于序列上的区间询问问题,如果从\([l,r]\)的答案能够\(O(1)\)扩展到\([l-1,r]\)\([l+1,r]\)\([l,r-1]\)\([l,r+1]\)(即与\([l,r]\)相邻的区间)的答案,那么可以在\(O(n\sqrt{n})\)的复杂度内求出所有询问的答案。实现¶离线后排序,顺序处理......
  • 莫队与分块
    【根号分治】例题:等差数列加给定一个长度\(n\)的数列,初始全都是0。(\(n\leq2\times10^5\))要求支持两种操作:\(1\;x\;y\;d\),表示把所有下标模\(x\)等于\(y\)的位置全部加上\(d\);\(2\;x\),表示查询\(a_x\)当前值。做法:对于所有\(x>\sqrtn\),我们直接暴力循环......
  • 『学习笔记』莫队
    Part0.目录概念普通莫队树上莫队带修莫队Part1.概念莫队是由莫涛提出的算法。莫队算法可以解决一类离线区间询问问题,适用性极为广泛。Part2.普通莫队普通莫队主要针对于多次区间询问的问题,基于分块的思想。过程如下:先将当前区间\([l,r]\)设为\([1,0]\),再每次......