[带修莫队] Bzoj 2120 数颜色

Posted comfortable

tags:

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

Description

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?

Input

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

Output

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

Sample Input

6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6

Sample Output

4
4
3
4

HINT

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

 

题解

  • 可修改的莫队,怎么做呢?

  • 普通的莫队做法肯定会爆炸

  • 我们可以先引入一个“修改时间”,表示当前询问是发生在前Time个修改后的。

  • 也就是说我们在做莫队的时候除了原先的两个l,r指针,还引入时间指针,可以通过时间倒流或时光推移来保证正确性

  • 按照原先的sort函数来说,使得每个询问最坏情况下时间指针可以移动n次,总共就n^2次,极其不优秀,那么下面我们就要用三关键字排序来做

  • 时间复杂度分析:(num=sqrt(n))

  • ①对于l指针,依旧是O(num*n)

  • ②对于r指针,也依旧是O(n*n/num)

  • ③对于t指针(时间指针):

  • 我们要寻找有多少个单调段(一个单调段下来最多移动n次) 在排序函数里,当且仅当两个询问l在同块,r也在同块时,才会对可怜的t进行排序。

  • 对于每一个l的块,里面r最坏情况下占据了所有的块

  • 所以最坏情况下:有n/num个l的块,每个l的块中会有n/num个r的块,此时,在一个r块里,就会出现有序的t。

  • 所以t的单调段个数为:(n/unit)^2。

  • 每个单调段最多移动n次。 最后所以时间指针的时间复杂度为 O((n/num)^2*n)

  • 三个指针汇总:O(num*n+n^2/num+(n/num)^2*n)。当num=2/3n时,时间最优秀,为O(n^3/5)

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cmath>
 5 #define N 10010
 6 using namespace std;
 7 struct Query{int l,r,t,d;}q[N];
 8 struct Change{int w,l,r;}p[N];
 9 int n,m,ans,l=1,r,k,a[N],col[N*100],Time,numq,now[N],num,bel[N],Ans[N];
10 bool cmp(Query a,Query b) { return bel[a.l]==bel[b.l]?(bel[a.r]==bel[b.r]?a.t<b.t:a.r<b.r):a.l<b.l; }
11 void change(int x,int y) { col[x]+=y; if (y>0) ans+=col[x]==1; if (y<0) ans-=col[x]==0; }
12 void pd(int x,int y) { if (l<=x&&x<=r) change(y,1),change(a[x],-1); a[x]=y; }
13 int main()
14 {
15     scanf("%d%d",&n,&m),num=pow(n,0.66666);
16     for (int i=1;i<=n;i++) scanf("%d",&a[i]),now[i]=a[i],bel[i]=i/num+1;
17     for (int i=1,x,y;i<=m;i++)
18     {
19         char ch;
20         scanf(" %c %d%d",&ch,&x,&y);
21         if (ch==Q) q[++numq]=(Query){x,y,Time,numq};
22         if (ch==R) p[++Time]=(Change){x,y,now[x]},now[x]=y;
23     }
24     sort(q+1,q+numq+1,cmp);
25     for (int i=1;i<=numq;i++) 
26     {
27         while (k<q[i].t) pd(p[k+1].w,p[k+1].l),k++;
28         while (k>q[i].t) pd(p[k].w,p[k].r),k--;
29         while (l<q[i].l) change(a[l],-1),l++;
30         while (l>q[i].l) change(a[l-1],1),l--;
31         while (r<q[i].r) change(a[r+1],1),r++;
32         while (r>q[i].r) change(a[r],-1),r--;
33         Ans[q[i].d]=ans;
34     }
35     for (int i=1;i<=numq;i++) printf("%d
",Ans[i]);
36 }

 

以上是关于[带修莫队] Bzoj 2120 数颜色的主要内容,如果未能解决你的问题,请参考以下文章

[带修莫队] Bzoj 2120 数颜色

bzoj2120: 数颜色

2120: 数颜色(带修莫队)

bzoj 2120: 数颜色带修改莫队

Luogu1903数颜色(带修莫队)

数颜色 / 维护队列(带修莫队板子)