莫队阶段小结

莫队阶段小结

首先,为什么要叫小结呢,因为我只学了一点点,后续可能更多

莫队

莫队是一种离线处理区间问题的神器.答题思路就是你将原数列分成\(\sqrt{n}\)块,将所有查询左端点定位,并按照左端点所在的块进行排序,相同则按照右端点排序

大体就是这个样子

inline bool cmp(Q x,Q y){
    return belong[x.li] == belong[y.li] ? x.ri < y.ri : x.li < y.li;
}

这样的话我们每个快内都暴力求

时间复杂度为\(O(m\sqrt{n})\)

但是还有一种玄学加速方式,十分有用

inline bool cmp(Q x,Q y){
    return belong[x.li] ^ belong[y.li] ? belong[x.li] < belong[y.li] : belong[x.li] & 1 ? x.ri < y.ri : x.ri > y.ri;
}

会快很多.

之后我们每次维护取件区间,和当前左右端点.每次发现不在当前区域内就暴力跳的同时修改信息

  int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i){
        int L = q[i].li,R = q[i].ri;
        while(nowl > L) add(--nowl);
        while(nowr < R) add(++nowr);
        while(nowl < L) del(nowl++);
        while(nowr > R) del(nowr--);
        //统计答案
    }

另外注意应当先拓展后收缩,为了该开始改成负数之类的东西。

接下来放几道例题

luoguP3901 数列找不同

莫队的板子题了,我们维护\(sum_i\)表示\(i\)这个数当前出现了多少次,\(ans\)表示当前出现次数\(>=2\)的数的个数

每次修改时更新\(sum\)和\(ans\),之后统计答案即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int N = 1e5 + 3;
int a[N],ans[N];
int sum[N];
int n,m,now = 0,bl;
int belong[N],block;
inline int read(){
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar();
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar();
    }
    return v * c;
}
struct Q{
    int li,ri;
    int id;
}q[N];
inline bool cmp(Q x,Q y){
    return belong[x.li] == belong[y.li] ? x.ri < y.ri : x.li < y.li;
}
inline void add(int x){
    x = a[x];
    ++sum[x];
    if(sum[x] == 2) now++;
}
inline void del(int x){
    x = a[x];
    --sum[x];
    if(sum[x] == 1) now--;
}
int main(){
    n = read(),m = read();
    for(int i = 1;i <= n;++i) a[i] = read();
    bl = sqrt(n);
    for(int i = 1;i <= n;++i) belong[i] = i / bl;
    for(int i = 1;i <= m;++i) q[i].li = read(),q[i].ri = read(),q[i].id = i;
    sort(q + 1,q + m + 1,cmp);
    int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i){
        int L = q[i].li,R = q[i].ri;
        while(nowl < L) del(nowl++);
        while(nowl > L) add(--nowl);
        while(nowr > R) del(nowr--);
        while(nowr < R) add(++nowr);
        ans[q[i].id] = now;
    }
    for(int i = 1;i <= m;++i){
        if(ans[i]) printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}

CF375D Tree and Queries

虽然是树上问题,但是所有的询问都是针对子树,我们就可以针对他的欧拉序进行操作.
但是比较棘手的是所有的\(k_i\)可能不相同,

我们就设\(sum_i\)表示\(>=i\)的数的个数

\(val_i\)表示\(i\)的出现次数

每次修改统计即可

答案就是\(sum[k_i]\)

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<vector>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int N = 1e5 + 3;
int n,m;
vector <int> G[N];
int v[N],sum[N],val[N];
int l[N],r[N],c[N];
int belong[N];
int ans[N],tot,cnt,bl;
struct Q{
    int li,ri;
    int id;
    int ko;
}q[N];
inline int read(){
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar();
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar();
    }
    return v * c;
}
inline void dfs(int x,int f){
    c[++cnt] = v[x];
    l[x] = cnt;
    for(int i = 0;i < (int)G[x].size();++i){
        int y = G[x][i];
        if(y == f) continue;
        dfs(y,x);
    }
    r[x] = cnt;
}
inline bool cmp(Q x,Q y){
    return belong[x.li] == belong[y.li] ? x.ri < y.ri : x.li < y.li;
}
//sum[i]:>=i的数的种类数
//val[i]:i出现次数
inline void add(int x){
    x = c[x];
    val[x]++;
    sum[val[x]]++;
}
inline void del(int x){
    x = c[x];
    sum[val[x]]--;
    val[x]--;
}
int main(){
    n = read(),m = read();
    for(int i = 1;i <= n;++i) v[i] = read();
    for(int i = 1;i < n;++i){
        int x = read(),y = read();
        G[x].push_back(y);G[y].push_back(x);
    }
    dfs(1,0);
//  cout << "GG" << endl;
//  for(int i = 1;i <= n;++i) printf("%d %d\n",l[i],r[i]);cout << "GG"  << endl;
    bl = sqrt(n);
    for(int i = 1;i <= n;++i) belong[i] = i / bl;
    for(int i = 1;i <= m;++i){
        int x = read();
        q[i].li = l[x];
        q[i].ri = r[x];
        q[i].ko = read();
        q[i].id = i;
    }
    sort(q + 1,q + m + 1,cmp);
    int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i){
        int L = q[i].li,R = q[i].ri;
        while(nowl > L) add(--nowl);
        while(nowl < L) del(nowl++);
        while(nowr < R) add(++nowr);
        while(nowr > R) del(nowr--);
        ans[q[i].id] = sum[q[i].ko];
    }
    for(int i = 1;i <= m;++i) printf("%d\n",ans[i]);
    return 0;
}

luoguP4396 [AHOI2013]作业

这道题和上几道不大一样,发现不了区间限制之外,还有值域的限制

我们就用树状数组维护值域

但是这样的时间复杂度为\(n\sqrt{n}logn\)

其实跑\(10^5\)是挺虚的,还好题目\(3s\),这道题有\(n\sqrt{n}\)的做法,先咕一咕

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 1e5 + 3;
int a[N],ans[N],belong[N];
int ans2[N],ans1[N];
int n,m,bl;
int maxx;
int book[N];
struct Q{
    int li,ri;
    int ai,bi;
    int id;
}q[N << 1];
struct BIT{
    int c[N << 3];
    inline void add(int x,int v){
        for(;x <= maxx;x += x & -x) c[x] += v;
    }
    inline int query(int x){
        int res = 0;
        for(;x;x -= x & -x) res += c[x];
        return res;
    }
};
BIT T1,T2;
inline int read(){
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar();
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar();
    }
    return v * c;
}
inline bool cmp(Q x,Q y){
    return belong[x.li] ^ belong[y.li] ? belong[x.li] < belong[y.li] : belong[x.li] & 1 ? x.ri < y.ri : x.ri > y.ri;
}
//int ans = 0;
inline void add(int x){
    x = a[x];
    if(book[x] == 0) T2.add(x,1);
    book[x]++;
    T1.add(x,1);
}
inline void del(int x){
    x = a[x];
    if(book[x] == 1) T2.add(x,-1);
    book[x]--;
    T1.add(x,-1);
}
int main(){
    n = read(),m = read();
    bl = sqrt(n);
    for(int i = 1;i <= n;++i) {
        a[i] = read() + 1,belong[i] = i / bl;
        maxx = max(maxx,a[i]);
    }
    for(int i = 1;i <= m;++i){
        q[i].li = read(),q[i].ri = read();
        q[i].ai = read() + 1,q[i].bi = read() + 1;
        if(q[i].ai > q[i].bi) swap(q[i].ai,q[i].bi);
        q[i].id = i;
        maxx = max(q[i].ai,max(maxx,q[i].bi));
    }
    sort(q + 1,q + m + 1,cmp);
    int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i){
        int L = q[i].li,R = q[i].ri;
        while(nowl > L) add(--nowl);
        while(nowr < R) add(++nowr);
        while(nowl < L) del(nowl++);
        while(nowr > R) del(nowr--);
        ans1[q[i].id] = T1.query(q[i].bi) - T1.query(q[i].ai - 1);
        ans2[q[i].id] = T2.query(q[i].bi) - T2.query(q[i].ai - 1);
    }
    for(int i = 1;i <= m;++i) printf("%d %d\n",ans1[i],ans2[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/wyxdrqc/p/10982394.html

时间: 2024-10-03 22:49:13

莫队阶段小结的相关文章

# 莫队算法小结(待更新)

目录 莫队算法小结(待更新) 简单介绍 基础莫队 带修莫队 树上莫队 莫队算法小结(待更新) 简单介绍 博客安利: OI Wiki 大米饼 解决一类离线区间查询问题,分块思想,时间复杂度\(O(n\sqrt n)\) 排序 读入的时候对整个数组进行分块,块大小一般使用\(\sqrt n\),对询问操作排序的时候,先以块号为第一关键字,\(r\)为第二关键字,从小到大排序,然后逐个遍历 离线后将询问排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间的答案(通过两个指针的\(++\)和\

莫队算法小结(Markdown版)

wtf,最近挖坑有点小多啊,没办法>_<容我先把糖果公园A了再来写这个吧= =看看今天能不能A掉 好吧,我承认我第二天才把糖果公园A掉>_<下面把这篇小结补上 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m个询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为0则输出01 仔细观察发现,令x表示x这个值出现的次

莫队算法小结

唔,想有更加舒爽的阅读体验请移步http://mlz000.logdown.com/posts/252433-mo-algorithm-summary 首先众所周知的是莫队算法是要把询问先按左端点属于的块排序,再按右端点排序 复杂度就先不证了,有兴趣的同学可以自己YY下或者查阅资料 下面举几个例子详细说明 1.小Z的袜子 Description: 给定一个序列m询问 每次询问: 区间中选两个数,两个数相等的概率 若概率为则输出0/1 仔细观察发现,令x表示x个值出现的次数,则每次询问[l,r]区

莫队算法小结以及模板题

被splay折磨了一个星期还是抄模板都wa,调试的时候查数据都发现数据太大不能查的蒟蒻杨澜决定学习莫队算法, 但是 万万没想到 莫队也到处都是坑 ----------------------------------------------题记 莫队这个东西就是一个非常简♂单的类似动规的东西,也是那种不会就一直不会,一看题解秒懂的题,[虽然我看题解也半天看不懂但是只是第一道题,万事开头难嘛(也许吧)] 莫队算法是一种基于分块的离线算法,主要用来解决一些区间的询问[就是暴力],我们需要做的就是把询问

[bzoj3289]Mato的文件管理_莫队_树状数组

Mato的文件管理 bzoj-3289 题目大意:给定一个n个数的序列.m次询问:一段区间中的逆序对个数. 注释:$1\le n\,mle 5\cdot 10^4$. 想法: 开始想这个题的大佬们,给您点儿提示吧:$O(nlogn\sqrt(n))$可过哦! 所以这个题就是莫队的裸题了. 我们的莫队上的区间在动的时候随时更新树状数组上的信息即可.. 然后碰见了一整块区间,我们就直接求逆序对即可, 最后,附上丑陋的代码... ... #include <iostream> #include &l

[bzoj2453]维护队列_带修改莫队

维护队列 bzoj-2453 题目大意:给定一个n个数序列,支持查询区间数的种类数,单点修改.不强制在线. 注释:$1\le n,m\le 10^5$. 想法: 带修改莫队裸题. 如果没有修改操作的话,我们就正常按照莫队一样左右移动区间即可. 有了修改操作的话,我们把块变成$n^{\frac{2}{3}}$,关键字变成:左端点所在块.右端点所在块和时间戳. 然后暴力就行了. Code: #include <iostream> #include <cstdio> #include &

(莫队算法)CodeForces - 617E XOR and Favorite Number

题意: 长度为n的数列,m次询问,还有一个k.每次询问询问询问从数列的L到R内有多少个连续子序列异或起来等于k. 分析: 因为事先知道这题可以用莫队写,就正好用这题练习莫队. 预处理每个前缀异或和. 然后莫队按分块排序后,不断更新,用一个数组cnt[]记录当前L到R前缀和的数量. R向右拉,新增的数量就是cnt[pre^k],pre表示当前这个R位置的前缀异或和,然后更新一下cnt. 其他的也类似. 算是一个比较好的入门题. 代码: 1 #include <cstdio> 2 #include

莫队算法

Beautiful Girl 题意 给定一个长度为 n 的序列 a[1], a[2], ..., a[n] . m 组询问 (l, r, K) , 求区间 [l, r] 去除重复的数之后的第 K 小. n, m <= 100000 . 分析 莫队算法 + 值域分块. 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include &

BZOJ4241 历史研究 莫队算法 堆

欢迎访问~原文出处--博客园-zhouzhendong&AK 去博客园看该题解 题目 Description IOI国历史研究的第一人--JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1.