内容简介:今天学习了回滚莫队,忍不住手痒写一下学习笔记。(bushi回滚莫队其实十分的简单易懂。为什么需要回滚莫队呢?
回滚莫队
今天学习了回滚莫队,忍不住手痒写一下学习笔记。(bushi
回滚莫队其实十分的简单易懂。
为什么需要回滚莫队呢?
普通的莫队可以O(1)维护从(L , R)转移到(L ± 1 , R) , (L , R ± 1),但是,有一些维护值 扩展容易收缩难,或者 收缩容易扩展难。
比如,最大值的维护就是扩展容易收缩难。举个例子:我们要从(L , R)转移到(L+1 , R),那么a L 就不在区间中了,可是我们很难判断a L 是否为(L , R)中的最大值,如果是,且唯一,则(L+1 , R)的最大值就不再是a L ,因为a L 已经被移出区间,我们也不知道(L+1 , R)的最大值究竟是多少。这样,就无法转移了。
此时, 回滚莫队 闪亮登场!
回滚莫队的思想
只在左端点在同一分块内时才转移。当左端点在新的分块内时,初始化所有。
因为左端点在同一分块内的是按右端点排序,所以右端点肯定是单向扩展或收缩的,像普通莫队一样扩展即可。
左端点是乱序的,所以每次都从当前分块的右边界开始往左边扩展到左端点,这样就只会扩展而不会面临收缩的局面(如果是收缩容易扩展难就从左边界开始收缩)。
时间复杂度依然是O(n√n)。
回滚莫队的具体实现(扩展容易收缩难为例)
1. 对原序列进行分块,并对询问按照如下的方式排序:以左端点所在的块升序为第一关键字,以右端点升序为第二关键字。( 注意:我们不能使用奇偶优化 )
2. 遍历所有分块。
3. 我们先将莫队当前分块区间左端点初始化为分块的右边界+1,右端点初始化为分块块的右边界,这是一个空区间。
4. 左右端点在同一个分块中的询问 ,我们直接暴力遍历即可。然后再遍历一次修改回原样。
5. 左右端点不在同一个分块中的询问 ,一直扩展莫队区间右端点直到区间右端点与询问右端点重合。区间左端点每次都从当前分块的右边界开始往左边扩展到左端点。然后再把区间左端点扫回分块右边界+1,把所有数据修改回原样。(回滚)
6.所有左端点在当前分块内的询问遍历结束后,清空所有数据。(就是把区间右端点扫回分块右边界)(回滚)
下面,我们来看一道板子题:
洛咕 AT1219 历史研究
题目描述
IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。
N)发生的事件的种类用一个整数X i 表示,X i 越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:
1.选择日记中连续的一些天作为分析的时间段
2.事件种类t的重要度为t*(这段时间内重要度为t的事件数)
3.计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
输入格式
第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。 接下来一行N个空格分隔的整数 X 1 ...X N
输出格式
输出Q行,第i行( 1 ≤ i ≤ Q)一个整数,表示第i次询问的最大重要度
样例
输入样例
输出样例
数据范围与提示
1 ≤ N ≤ 1 0 5 ,
1 //回滚莫队
2 //洛咕AT1219 历史研究
3
4 #include <iostream>
5 #include <algorithm>
6 #include <cstdio>
7 #include <cstring>
8 #include <cmath>
9 #define ll long long
10 #define inf0x3f3f3f3f
11 using namespace std;
12 int read(){
13 int ret=0,ttt=1;
14 char ch=getchar();
15 while(ch<'0' || ch>'9'){
16 if(ch=='-'){
17 ttt=-1;
18 }ch=getchar();
19 }while(ch>='0' && ch<='9'){
20 ret=(ret<<1)+(ret<<3)+(ch^48);
21 ch=getchar();
22 }return ret*ttt;
23 }
24 struct dat{
25 int l,r,p; //询问左端点、右端点、询问编号(方便在 排序 后按输入顺序输出答案)
26 };
27 int n,m,sq;
28 int a[100005],pos[100005],num[100005],v[100005],b[100005]; //a 种类, b 存原来的a, v 离散值, pos[i] i属于的分块号, num 种类出现次数
29 ll ans[100005];
30 dat no[100005]; //询问
31 bool cmp(dat u, dat v){
32 if(pos[u.l]==pos[v.l]){
33 return u.r<v.r;
34 }return u.l<v.l;
35 }
36 int tt;
37 void in(){ //离散化(莫队不需要,是由于题目原因)
38 sort(a+1,a+n+1);
39 tt=n;
40 tt=unique(a+1,a+tt+1)-(a+1);
41 for(int i=1; i<=n; i++){
42 v[i]=lower_bound(a+1,a+tt+1,b[i])-a;
43 }
44 }
45 int main(){
46 n=read(),m=read();
47 sq=int(sqrt(n));
48 int sum=0,cnt=0;
49 for(int i=1; i<=n; i++){
50 a[i]=read();
51 b[i]=a[i]; //离散化
52 if(i>sum){
53 sum+=sq;
54 cnt++;
55 }pos[i]=cnt;
56 }in();
57 for(int i=1; i<=m; i++){
58 no[i].l=read(),no[i].r=read();
59 no[i].p=i;
60 }sort(no+1,no+m+1,cmp);
61 int p=1;
62 for(int k=1; k<=n; k+=sq){ //遍历所有块
63 int you=min(k+sq-1,n),zuo=you+1; //莫队区间左右端点
64 ll now=0,tmp=0;
65 for(int j=p; j<=m; j++){
66 int l=no[j].l,r=no[j].r;
67 if(l>min(k+sq-1,n)){
68 p=j;
69 break;
70 }if(j==m){
71 p=m+1;
72 }if(pos[l]==pos[r]){ //特判:询问左右端点在同一块中
73 for(int i=l; i<=r; i++){ //暴力扫
74 num[v[i]]++;
75 now=max(now,1ll*b[i]*num[v[i]]);
76 }ans[no[j].p]=max(ans[no[j].p],now);
77 now=tmp;
78 for(int i=l; i<=r; i++){
79 num[v[i]]--; //回滚
80 }continue;
81 }while(you<r){ //扩展右端点
82 you++;
83 num[v[you]]++;
84 now=max(now,1ll*b[you]*num[v[you]]);
85 }tmp=now; //tmp记录此时的now值
86 while(zuo>l){ //扩展左端点
87 zuo--;
88 num[v[zuo]]++;
89 now=max(now,1ll*b[zuo]*num[v[zuo]]);
90 }ans[no[j].p]=max(ans[no[j].p],now);
91 now=tmp; //回滚now值
92 while(zuo<k+sq){ //回滚左端点
93 num[v[zuo]]--;
94 zuo++;
95 }
96 }while(you>k+sq-1){ //整个块遍历结束,回滚右端点
97 num[v[you]]--;
98 you--;
99 }
100 }for(int i=1; i<=m; i++){
101 printf("%lld\n",ans[i]);
102 }
103 return 0;
104 }
以上所述就是小编给大家介绍的《回滚莫队学习笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【每日笔记】【Go学习笔记】2019-01-04 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-02 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-07 Codis笔记
- Golang学习笔记-调度器学习
- Vue学习笔记(二)------axios学习
- 算法/NLP/深度学习/机器学习面试笔记
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。