Mo's Algorithm——莫队算法

<一个优雅的暴力算法>

主要题型分为三种,不带修改莫队,带修改莫队,树形莫队。

<最最最最最普通的莫队>

莫队算法实质是离线情况下对所有即将要查询的区间进行一次美妙的sort(以L为第一关键字,R为第二关键字),然后用

两个指针L,R,在区间上用看似暴力的方法跳来跳去,最终输出答案。

详解sort:有人会问,为什么不直接按L排序呢,但你想想,若L<L1<L2,且R1<R<R2,那么如果直接按L排序,就会

浪费很多时间,所以这里我们引入了一个分块思想,每个块的大小为sqrt(N),分块使得两个询问之间的差异平均到了L,

R上,理论复杂度为O(N*sqrt(N))。

我们应如何状态转移区间?当前状态下,询问区间为[a,b],下一个询问区间为[p,q],当前已知区间[a,b]内每种颜色

的出现次数cnt[],已知询问区间[a,b]的答案ans1,怎么求出ans2呢?

每一次移动l,r,我们都需要维护cnt数组和ans。

如图,我们损失了一个绿色方块,那么我们需对cnt[绿]减一,并判断是否为零,若为零,则表示当前区间内已经没有绿色了。

所以ans减一,否则不变。

再如图,我们得到了一个橙色方块,那么cnt[橙]+1,我们还需判断cnt[橙]加后是否为一,若是的话,则表示在当前区间新增了

一种颜色,那么ans+1,否则不变。

当然,以上只列举了两种情况,其实有四种情况不过本蒟蒻比较懒。

懂了的话可以去完成luogu2709,这里给出代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn 50050
using namespace std;
struct Query{int near,l,r,id;
}Q[maxn];
int cnt[maxn],where[maxn],ans=0,a[maxn],Qnum,Cnum,print[maxn],k,m;
int read(){int s=0;char c=getchar();while(c<'0'||c>'9') c=getchar();while(c>='0'&&c<='9') s=s*10+c-48,c=getchar();return s;
}
bool cmp(Query a,Query b){if(where[a.l]!=where[b.l]) return where[a.l]<where[b.l];
return a.r<b.r;
}
void Add(int val){ans+=(cnt[val]++)<<1|1;
}
void Delet(int val){ans-=(--cnt[val])<<1|1;
}
void MoQueue(){int l=1,r=0,now=0;for(int i=1;i<=Qnum;i++){while(l<Q[i].l) Delet(a[l++]);while(l>Q[i].l) Add(a[--l]);while(r<Q[i].r) Add(a[++r]);while(r>Q[i].r) Delet(a[r--]);print[Q[i].id]=ans;}for(int i=1;i<=m;i++){printf("%d\n",print[i]);}
}
int main(){int n,base;char opt;n=read();m=read();k=read();base=sqrt(n);for(int i=1;i<=n;i++) a[i]=read(),where[i]=i/base+1;int mm=m;while(mm--){Q[++Qnum].l=read();Q[Qnum].r=read();Q[Qnum].near=Cnum;Q[Qnum].id=Qnum;}sort(Q+1,Q+1+Qnum,cmp);MoQueue();return 0;
}

小B的询问

<带修改的莫队>

也称可持久化莫队。

在这里我们引入第三个关键字——修改时间,即当前查询是在经过第几次修改之后的,也就是说,在查询时,

看看当前询问和时间指针,进行时间倒流或时间推移。所以sort的构造也要改变,若还是按原来,可能会因时间的影响

而移动n次,导致时间复杂度爆炸级增长,所以我们引入了第三个关键字——时间。

bool cmp(Query a,Query b){if(where[a.l]!=where[b.l]) return where[a.l]<where[b.l];if(a.r!=b.r) return a.r<b.r;return a.near<b.near;
}

sort

我们用个time来记录当前是第几次修改了,若当前要查询的区间的最近的修改位置比time还要后,我们则需把

中间从time+1到里查询区间最近的一次补上,若比time还要前,我们需把查询区间最近的一次+1到time还原。

luogu1903:

#include<cstdio>
#include<algorithm>
#include<cmath>
#define maxn 10050
using namespace std;
struct Query{int near,l,r,id;
}Q[maxn];
struct Change{int pos,val;
}C[1050];
int cnt[maxn],where[maxn],ans=0,a[maxn],Qnum,Cnum,print[maxn];
int read(){int s=0;char c=getchar();while(c<'0'||c>'9') c=getchar();while(c>='0'&&c<='9') s=s*10+c-48,c=getchar();return s;
}
bool cmp(Query a,Query b){if(where[a.l]!=where[b.l]) return where[a.l]<where[b.l];if(a.r!=b.r) return a.r<b.r;return a.near<b.near;
}
void Add(int val){if(++cnt[val]==1) ans++;
}
void Delet(int val){if(--cnt[val]==0) ans--;
}
void Work(int now,int i){if(C[now].pos>=Q[i].l&&C[now].pos<=Q[i].r){if(--cnt[a[C[now].pos]]==0) ans--;if(++cnt[C[now].val]==1) ans++;}swap(C[now].val,a[C[now].pos]);
}
void MoQueue(){int l=1,r=0,now=0;for(int i=1;i<=Qnum;i++){while(l<Q[i].l) Delet(a[l++]);while(l>Q[i].l) Add(a[--l]);while(r<Q[i].r) Add(a[++r]);while(r>Q[i].r) Delet(a[r--]);while(now<Q[i].near) Work(++now,i);while(now>Q[i].near) Work(now--,i);print[Q[i].id]=ans;}for(int i=1;i<=Qnum;i++){printf("%d\n",print[i]);}
}
int main(){int n,m,base;char opt;n=read();m=read();base=sqrt(n);for(int i=1;i<=n;i++) a[i]=read(),where[i]=i/base+1;while(m--){scanf(" %c",&opt);if(opt=='Q'){Q[++Qnum].l=read();Q[Qnum].r=read();Q[Qnum].near=Cnum;Q[Qnum].id=Qnum;}else{C[++Cnum].pos=read();C[Cnum].val=read();}}sort(Q+1,Q+1+Qnum,cmp);MoQueue();return 0;
}

带修莫队

至于树形莫队嘛,本蒟蒻还没学,敬请期待!

转载于:https://www.cnblogs.com/Fish-/p/8228215.html

Mo's Algorithm相关推荐

  1. 【CF 617E】 XOR and Favorite Number (Mo's algorithm)

    [CF 617E]  XOR and Favorite Number (Mo's algorithm) E. XOR and Favorite Number time limit per test 4 ...

  2. 莫队算法 ( MO's algorithm )

    莫队算法是由清华大学神牛莫涛发明的一种处理区间问题的离线算法 算法核心是通过先将问询区间总长度平方分块.然后将所有的问询区间按照左端点所在的块编号排序.在同一块内的则按右端点升序 然后设置左右两个下标 ...

  3. 分治 —— 莫队算法

    [概述] 莫队算法(mo's algorithm)是用来解决离线区间不修改询问问题,可以将复杂度优化到 O(n^1.5),除去普通的莫队算法外,还有带修改的莫队.树上莫队等等. 莫队常用于维护区间答案 ...

  4. ACM-ICPC模板整理

    备注其一:正在整理中,内容不全,部分代码测试次数较少或还未在OJ上尝试,可能会有代码不健全的情况发生. 备注其二:部分图片来自百度百科.wiki百科. 备注其三:CSDN一天只能上传十篇blog... ...

  5. 分块与莫队(详详详解)

    以下内容主要借鉴oiwiki 分块思想 简介 分块是一种思想,而不是一种数据结构. 分块的基本思想是:通过对原数据的适当划分,并在划分后的每一个块上预处理部分信息,从而较一般的暴力算法取得更优的时间复 ...

  6. 第 46 届国际大学生程序设计竞赛(ICPC)亚洲区域赛(南京),签到题5题

    文章目录 A.Oops, It's Yesterday Twice More M.Windblume Festival C.Klee in Solitary Confinement H.Crystal ...

  7. 【笔记篇】莫队算法(一)

    P.S.:这个星期写了一个星期的莫队,现在也差不多理解了,下周该学点别的了(其实是被long long卡得生活不能自理......快要写吐了). 在本文开始之前,先orz莫涛-- 莫队算法(Mo's ...

  8. 线段树详解 (原理,实现与应用)

    线段树详解 By 岩之痕 目录: 一:综述 二:原理 三:递归实现 四:非递归原理 五:非递归实现 六:线段树解题模型 七:扫描线 八:可持久化 (主席树) 九:练习题 一:综述 假设有编号从1到n的 ...

  9. 【转载】线段树题目2

    1.hdu1166 敌兵布阵 更新节点,区间求和. 2.hdu1754 I Hate It 更新节点,区间最值. . 3.hdu1698 Just a Hook 成段更新,总区间求和. . 4.hdu ...

最新文章

  1. 原型和构造函数(2)
  2. linux上的项目可以在windows,新建的django项目,在linux系统可以跑,在windows上跑老是报错...
  3. LeetCode Algorithm 559. N 叉树的最大深度
  4. Codeforces Beta Round #1--C题(多边形求最小面积)
  5. IPython快捷键及命令
  6. 11 操作系统第三章 内存管理 内存的基本知识 内存管理 内存空间扩充 连续分配管理方式
  7. LeetCode--26. 删除排序数组中的重复项(双指针,暴力)
  8. 智能机器人建房子后房价走势_重磅!机器人建房时代将至,工作效率是人的4倍,有助房价下跌?...
  9. Win7 系统下配置WinCE 5.0 模拟器网络环境
  10. 深耕智慧互联网,华数传媒正重新定义生活方式
  11. R语言将数据导出到csv时出现科学计数表示
  12. HTML5响应式手机模板:电商网站设计——房地产移动端网页模板(15个页面)
  13. 上传csv文件后出现乱码
  14. [C语言] for循环详解 循环 for循环实例巧解
  15. 智慧管廊综合管理系统设计方案
  16. Debian11安装Proxmox VE 7实操记录
  17. jperf linux运行,Linux网络性能评估工具iperf
  18. MATLAB绘制概率密度函数
  19. 快递分拣机器人系统运用了哪些计算机技术,自动分拣十几秒完成 快递机器人何日满城“跑”...
  20. python Lambda表达式和嵌套if-else

热门文章

  1. 李飞飞点赞!,刚刚公布了第一批数据集benchmark入围名单
  2. 李沐:工作五年反思!
  3. 自动生成代码:TensorFlow官方工具TF-Coder已开源
  4. python魔法方法(一)
  5. GBDT(梯度提升决策树)总结笔记
  6. mysql trigger 有时 不执行_Mysql 寒假刷题TIPs
  7. 看别人情侣空间显示服务器繁忙什么意思,调查13000位80后年轻人,他们的感情状态究竟如何?...
  8. 计算机适配器有什么作用,例举适配器是什么
  9. c语言中整形精确到后面几位,C语言中普通整型变数在记忆体中占几个位元组
  10. excel不显示0_【周一实用技巧】绝密,保护公式不被修改。Excel单元格保护可以输入但不能修改公式,隐藏不显示公式内容...