1. 程式人生 > >【[國家集訓隊]數顏色】

【[國家集訓隊]數顏色】

不知道 CP www 修改顏色 %d 討論 solution rev func

下面的同學們搞怪了

好的,話不多說,我來講這道題時順便就來講講帶修莫隊

不知道普通莫隊是什麽的請參見我之前為小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)^2

n)

當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;
}

【[國家集訓隊]數顏色】