带修改的莫队

Posted genius777

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了带修改的莫队相关的知识,希望对你有一定的参考价值。

在学习了最基础的莫队后,我们会发现普通莫队并不资瓷修改操作啊!!!这就很尴尬,那么莫队就没办法修改吗,反正当时发明莫队的人是没有提到的,但是不要小瞧了智慧的OI人,不久就有人就提出了带修改莫队的想法。

如果你还没不知道莫队 点击这里!!!

其实刚知道莫队还可以资瓷修改时,我以为代码量会有质的飞越,但是学习后才发现原来不过如此,所以就别担心了。。。

首先还是来一道题 数颜色

题目描述

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。

2、 R P Col 把第P支画笔替换为颜色Col。

为了满足墨墨的要求,你知道你需要干什么了吗?

输入输出格式

输入格式:

 

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。

第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。

第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

 

输出格式:

 

对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

 

输入输出样例

输入样例#1: 
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
输出样例#1: 
4
4
3
4

说明

对于100%的数据,N≤50000,M≤50000,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

本题可能轻微卡常数

来源:bzoj2120


 【带修改莫队???】

向比较于普通的莫队,我们多了修改操作,我们在普通莫队时离线了询问操作,所以这里我们把修改也离线下来,并且在之前的左右指针基础上,加一个时间戳,记录我们当前到了哪个时间点了,同时我们维护每个询问距离其最近的修改操作是什么。

当我们处理到一个区间时,我们判断当前时间点是否到了这个询问最近的修改操作的时间点,如果不符合那么我们就进行时间跳跃(是不是很秀的样子)然后顺便修改一下颜色,比起普通莫队就多一个这个。。。当然带了修改的莫队复杂度会高一点,毕竟时间戳也要跳啊!!!

 


 

接下来我们来简要地证明一下复杂度!(不是非常关心证明的同学,可以直接跳到结论部分……)

推移时间、移动左端点、移动右端点的操作都是O(1)的。

对于时间的移动,对于左右端点所在块不变的情况,时间是单调向右移的,总共O(n); 左右端点之一所在块改变,时间最多从n直接移回1,复杂度O(n);左右端点所在块各有O(n^1/3)种,两两组合有O(n^2/3)种,每种都是O(n),总复杂度是O(n^5/3)。

对于右端点的移动,在左右端点所在块不变时,每次最多移动n^2/3,一共最多有n次右端点的移动,复杂度是O(n^5/3);当左端点所在块改变时,右端点最多从n一直移动到1,距离是n,最多有n^1/3次这样的移动,复杂度是O(n^4/3);总共右端点移动的复杂度是O(n^5/3)。

对于左端点的移动,在左端点块不变时,一次移动距离最多n23,总共O(n53)。而跨块时,由于左端点所在块是单调向右移动的,复杂度最大的情况就是每跨一个块都是从前一个块的最左侧跑到后一个块的最右侧,距离O(n^1/3),总复杂度O(n)。所以总共左端点移动的复杂度是O(n^5/3)

结论:上述排序方法实现的莫队复杂度是O(n^5/3)。


 具体实现让我们在代码中细细体会

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 #include<iostream>
 5 using namespace std;
 6 inline int read()
 7 {
 8     char c=getchar();int x=0,f=1;
 9     while(c<\'0\'||c>\'9\'){if(c==\'-\')f=-1;c=getchar();}
10     while(c>=\'0\'&&c<=\'9\'){x=x*10+c-\'0\',c=getchar();}
11     return x*f;
12 }
13 const int maxn=5e4+5;
14 const int M=1e6+5;
15 struct Qurey{
16     int l,r,id,pre;
17 }que[maxn];
18 struct Change{
19     int pos,val;
20 }cc[maxn];
21 int col[maxn],vis[M],be[maxn],res[maxn],n,m,qnum,cnum,ans;
22 bool cmp(Qurey a,Qurey b)
23 {
24     if(be[a.l]!=be[b.l]) return be[a.l]<be[b.l];
25     if(be[a.r]!=be[b.r]) return be[a.r]<be[b.r];
26     return a.pre<b.pre;
27 }
28 void change(int v,int add)
29 {
30     if(add==-1) if(--vis[col[v]]==0) ans--;
31     if(add==1) if(++vis[col[v]]==1) ans++;
32 }
33 void movetime(int time,int v)
34 {
35     if(que[v].l<=cc[time].pos&&que[v].r>=cc[time].pos)
36     {
37         if(++vis[cc[time].val]==1) ans++;
38         if(--vis[col[cc[time].pos]]==0) ans--;
39     }
40     swap(cc[time].val,col[cc[time].pos]);
41 }
42 void moque()
43 {
44     int l=1,r=0,time=0;
45     for(int i=1;i<=qnum;i++)
46     {
47         while(l<que[i].l) change(l++,-1);
48         while(l>que[i].l) change(--l,1);
49         while(r<que[i].r) change(++r,1);
50         while(r>que[i].r) change(r--,-1);
51         while(time<que[i].pre) movetime(++time,i);
52         while(time>que[i].pre) movetime(time--,i);
53         res[que[i].id]=ans;
54     }
55     for(int i=1;i<=qnum;i++) printf("%d\\n",res[i]);
56 }
57 int main()
58 {
59     n=read(),m=read();
60     int k=pow(n,2.0/3);
61     for(int i=1;i<=n;i++)
62     col[i]=read(),be[i]=(i-1)/k+1;
63     for(int i=1;i<=m;i++)
64     {
65         char ord[5];
66         int a,b;
67         scanf("%s",ord);
68         a=read(),b=read();
69         if(ord[0]==\'Q\') 
70         que[++qnum].id=qnum,que[qnum].l=a,que[qnum].r=b,que[qnum].pre=cnum;
71         if(ord[0]==\'R\')
72         cc[++cnum].pos=a,cc[cnum].val=b;
73     }
74     sort(que+1,que+1+qnum,cmp);
75     moque();
76     return 0;
77 }

 

以上是关于带修改的莫队的主要内容,如果未能解决你的问题,请参考以下文章

带修改的莫队

BZOJ 2120: 数颜色 带修改的莫队算法 树状数组套主席树

更多的莫队

bzoj2120: 数颜色(带修改的莫队)

BZOJ 2120 数颜色&2453 维护队列 [带修改的莫队算法]学习笔记

BZOJ 2120 数颜色&2453 维护队列 [带修改的莫队算法]学习笔记