2120: 数颜色(带修莫队)
Posted caijiaming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2120: 数颜色(带修莫队)相关的知识,希望对你有一定的参考价值。
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2120
2120: 数颜色
Time Limit: 6 Sec Memory Limit: 259 MBSubmit: 10514 Solved: 4398
[Submit][Status][Discuss]
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
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
Sample Output
4
3
4
HINT
对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
2016.3.2新加数据两组by Nano_Ape
·述大意:
多个区间询问,询问[l,r]中颜色的种类数。可以单点修改颜色。
·分析:
莫队可以修改?那不是爆炸了吗。
这类爆炸的问题被称为带修莫队(可持久化莫队)。
按照美妙类比思想,可以引入一个“修改时间”,表示当前询问是发生在前Time个修改操作后的。也就是说,在进行莫队算法时,看看当前的询问和时间指针(第三个指针,别忘了l,r)是否相符,然后进行时光倒流或者时光推移操作来保证答案正确性。
·Sort的构造。仅靠原来的sort关键字会使得枚举每个询问都可能因为时间指针移动的缘故要移动n次,总共就n2次,那还不如写暴力。
·为了防止这样的事情发生,再加入第三关键字Tim:
·如何理解时间复杂度?
首先,R和Tim的关系就像L和R的关系一样:只有在前者处于同块时,后者才会得到排序的恩赐,否则sort会去满足前者,使得后者开始乱跳。
依旧像上文那样:枚举m个答案,就一个m了。设分块大小为unit。
分类讨论:
①对于l指针,依旧是O(unit*n)
②对于r指针,依旧是O(n*n/unit)
③对于T指针(即Time):
类比r时间复杂度的计算。我们要寻找有多少个单调段(一个单调段下来最多移动n次)。上文提到,当且仅当两个询问l在同块,r也在同块时,才会对可怜的Tim进行排序。局势明朗。对于每一个l的块,里面r最坏情况下占据了所有的块,所以最坏情况下:有n/unit个l的块,每个l的块中会有n/unit个r的块,此时,在一个r块里,就会出现有序的Tim。所以Tim的单调段个数为:(n/unit)*(n/unit)。每个单调段最多移动n次。
所以:O((n/unit)2*n)
三个指针汇总:O(unit*n+n2/unit+(n/unit)2*n)
代码:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int maxn=1000000+50; int block; int a[maxn],b[maxn],ans1[maxn]; int ans=0; struct Q { int x,y,num,tim; }q[maxn]; struct U { int x,y; }tw[maxn]; bool cmp(const Q a,const Q b) { if(a.x/block==b.x/block) { if(a.y/block==b.y/block) return a.tim<b.tim; return a.y/block<b.y/block; } return a.x/block<b.x/block; } void solve(int n,int add) { b[a[n]]+=add; if(b[a[n]]==1&&add==1) ans++; else if(b[a[n]]==0&&add==-1) ans--; } void time_charge(int n,int t) { if(q[n].x<=tw[t].x&&tw[t].x<=q[n].y) { b[a[tw[t].x]]--; if(b[a[tw[t].x]]==0) ans--; b[tw[t].y]++; if(b[tw[t].y]==1) ans++; } swap(tw[t].y,a[tw[t].x]); } int main() { int N,M,t=0,p1=0,p2=0; char c[10]; scanf("%d%d",&N,&M); block=pow(N,0.66666); for(int i=1;i<=N;i++) scanf("%d",&a[i]); for(int i=0;i<M;i++) { scanf("%s",c); if(c[0]==‘Q‘) { scanf("%d%d",&q[p1].x,&q[p1].y); q[p1].num=p1; q[p1].tim=t; p1++; } else { t++; scanf("%d%d",&tw[t].x,&tw[t].y); } } sort(q,q+p1,cmp); int L=1,R=0,T=0; for(int i=0;i<p1;i++) { while(R<q[i].y)//往右走 { solve(R+1,1); R++; } while(R>q[i].y) { solve(R,-1); R--; } while(L<q[i].x) { solve(L,-1); L++; } while(L>q[i].x) { solve(L-1,1); L--; } while(T<q[i].tim) { time_charge(i,T+1); T++; } while(T>q[i].tim) { time_charge(i,T); T--; } ans1[q[i].num]=ans; } for(int i=0;i<p1;i++) printf("%d\\n",ans1[i]); return 0; }
以上是关于2120: 数颜色(带修莫队)的主要内容,如果未能解决你的问题,请参考以下文章