[国家集训队]数颜色

Posted 66t6

tags:

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

下面的同学们搞怪了

好的,话不多说,我来讲这道题时顺便就来讲讲带修莫队

不知道普通莫队是什么的请参见我之前为小Z的袜子写的博客

首先来审审题意:多个区间询问,询问[l,r]中颜色的种类数。可以单点修改颜色。

莫队可以修改???

那么我们切入正题

这类问题被称为带修莫队(可持久化莫队)。

类比到主席树,即可持久化线段树,可以引入一个“修改时间”,表示当前询问是发生在前time个修改操作后的。也就是说,在进行莫队算法时,看看当前的询问和时间指针(第三个指针,别忘了s,e)是否相符,然后进行时光倒流(向前)或者时光推移(向后)操作来维护答案。

原本sort的构造。仅靠原来的sort关键字会使得枚举每个询问都可能因为时间指针移动的缘故要移动n次,总共就n^2次,那还不如写暴力。

所以为了防止这样的事情发生,大神们()再加入第三关键字tim:

   bool cmp(Node a,Node b)
   {
        return Be[a.l]==Be[b.l]?(Be[a.r]==Be[b.r]?a.tim<b.tim:a.r<b.r):a.l<b.l;
   }

Be数组的意义是记录分块大小

上次忘说了请见谅

接着就是时间复杂度

首先,r和tim的关系就像l和r的关系一样:只有在前后者处于同块时,后者才会因排序而有序,否则sort会去满足前者,使得后者开始乱跳。

依旧像上文那样:枚举m个答案,就一个m了。设分块大小为size。

分类讨论:

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

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

③对于tim指针:

类比r时间复杂度的计算。我们要寻找有多少个单调段(一个单调段下来最多移动n次)。上文提到,当且仅当两个询问l在同块,r也在同块时,才会对tim进行排序。对于每一个l的块,里面r最坏情况下占据了所有的块,所以最坏情况下:有n/size个l的块,每个l的块中会有n/size个r的块,此时,在一个r块里,就会出现有序的tim。所以tim的单调段个数为:(n/size)*(n/size)。每个单调段最多移动n次。

所以,其复杂度=O((n/size)2*n)

三个指针汇总:O(sizen+n^2/size+(n/size)^2n)

当size=n*sqrt(n)时,复杂度取到最高:

O(n^(5/3))!!!

接下来是代码

#include<stdio.h>
#include<algorithm>
#include<math.h>
#define go(i,a,b) for(int i=a;i<=b;i++)
using namespace std;const int N=10005;
struct Node{
int l,r,tim,ID;
}q[N];
struct Change{
int pos,now,bef;
}c[N];
int n,m,s[N],color[1000005],t,time,now[N],size,Be[N],ans[N],Ans,l=1,r,T;
bool cmp(Node a,Node b)
{
    return Be[a.l]==Be[b.l]?(Be[a.r]==Be[b.r]?a.tim<b.tim:a.r<b.r):a.l<b.l;
}
void revise(int x,int d)
{
color[x]+=d;
if(d>0)Ans+=color[x]==1;
else 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);size=pow(n,0.666666);
    go(i,1,n)scanf("%d",&s[i]),now[i]=s[i],Be[i]=i/size+1;
    go(i,1,m){char sign;int x,y;scanf(" %c %d%d",&sign,&x,&y);
        if(sign==‘Q‘)q[++t]=(Node){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(T<q[i].tim)going(c[T+1].pos,c[T+1].now),T++;
        while(T>q[i].tim)going(c[T].pos,c[T].bef),T--; 
        while(l<q[i].l)revise(s[l],-1),l++;
        while(l>q[i].l)revise(s[l-1],1),l--;
        while(r<q[i].r)revise(s[r+1],1),r++;
        while(r>q[i].r)revise(s[r],-1),r--;

        ans[q[i].ID]=Ans;
    }
    go(i,1,t)printf("%d\n",ans[i]);
return 0;
}

以上是关于[国家集训队]数颜色的主要内容,如果未能解决你的问题,请参考以下文章

[国家集训队]数颜色

国家集训队 数颜色 - 莫队

2120. [国家集训队]数颜色莫队

[莫队] 国家集训队 数颜色(尚未完成)

[国家集训队]数颜色 / 维护队列 (带修莫队模板题)

[国家集训队][bzoj2120] 数颜色 [带修改莫队]