#include
#include
#include
#define go(i,a,b) for(int i=a;i<=b;i++)
using namespace std;const int N=10003;
struct Query{int l,r,Tim,ID;}q[N];
struct Change{int pos,New,Old;}c[N];
int n,m,s[N],color[N*100],t,Time,now[N],unit,Be[N],ans[N],Ans,l=1,r,T;
bool cmp(Query a,Query b)
{
return Be[a.l]==Be[b.l]?(Be[a.r]==Be[b.r]?a.Tim0)Ans+=color[x]==1;if(d<0)Ans-=color[x]==0;}
void going(int x,int d){if(l<=x&&x<=r)revise(d,1),revise(s[x],-1);s[x]=d;}
int main(){
scanf("%d%d",&n,&m);unit=pow(n,0.666666);
go(i,1,n)scanf("%d",&s[i]),now[i]=s[i],Be[i]=i/unit+1;
go(i,1,m){char sign;int x,y;scanf(" %c %d%d",&sign,&x,&y);
if(sign=='Q')q[++t]=(Query){x,y,Time,t};
if(sign=='R')c[++Time]=(Change){x,y,now[x]},now[x]=y;
}
sort(q+1,q+t+1,cmp);go(i,1,t)
{
while(Tq[i].Tim)going(c[T].pos,c[T].Old),T--;
while(lq[i].l)revise(s[l-1],1),l--;
while(rq[i].r)revise(s[r],-1),r--;
ans[q[i].ID]=Ans;
}
go(i,1,t)printf("%d\n",ans[i]);return 0;
}//Paul_Guderian
·述大意:
多个区间询问,询问[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)
一定要sqrt(0.66666)
转载:https://www.cnblogs.com/Paul-Guderian/p/6933799.html