\(\\\)ios
原題題面太過混亂出題人語文涼涼c++
給出一個長爲 \(n\) 的數列 \(A\) ,屢次詢問:git
對於一個區間 \([L_i,R_i]\),把區間內的全部數最少劃分紅多少個數集,使得每個集合內沒有相同元素。spa
\(\\\)code
題目的模型很容易轉化成區間衆數問題。ip
莫隊求解區間衆數。get
首先數據範圍是假的,離散化以後就開的下桶了。string
對於區間擴張,確定是加一下桶,而後跟當前答案取 \(max\) 。it
問題在於區間縮小時,刪除一個數怎麼搞。io
\(\\\)
開始有一個 too simple 想法,是用堆去維護當前答案區間內全部數出現個數,而後懶惰刪除法,每次更新時判斷一下堆頂是否正確。
想想是對的,可是 \(O(N\sqrt NlogN)\) 的複雜度對於 \(2\times 10^5\) 很吃力。
\(\\\)
一個機智的作法。
設 \(cnt[i]\) 表示 \(bkt[x]=i\) 的個數,也就是當前區間裏出現次數爲 \(i\) 的數的個數。
空間沒有問題,由於最多出現數列長度的次數。
這樣一來修改就容易了不少,減的時候只須要判斷一下,當前數對應的 \(cnt\) 是否 \(>1\) 便可。
注意加減是對 \(cnt\) 和 \(bkt\) 的同時更新。
\(\\\)
#include<cmath> #include<cstdio> #include<cctype> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #define N 200000 #define R register #define gc getchar using namespace std; inline int rd(){ int x=0; bool f=0; char c=gc(); while(!isdigit(c)){if(c=='-')f=1;c=gc();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();} return f?-x:x; } int n,m,ans,bl[N],cnt[N],bkt[N],s[N],tmp[N]; struct Q{int l,r,ans,id;}q[N]; inline bool cmp1(Q x,Q y){ return bl[x.l]==bl[y.l]?x.r<y.r:bl[x.l]<bl[y.l]; } inline bool cmp2(Q x,Q y){return x.id<y.id;} inline void add(int p){ --cnt[bkt[s[p]]]; ++cnt[++bkt[s[p]]]; ans=max(ans,bkt[s[p]]); } inline void del(int p){ --cnt[bkt[s[p]]]; if(ans==bkt[s[p]]&&!cnt[bkt[s[p]]]) --ans; ++cnt[--bkt[s[p]]]; } int main(){ n=rd(); m=rd(); int t=sqrt(n),tot=0; for(R int i=1,cntt=1;i<=n;++i){ tmp[i]=s[i]=rd(); if(i%t==0) ++cntt; bl[i]=cntt; } sort(tmp+1,tmp+1+n); for(R int i=1;i<=n;++i){ tmp[++tot]=tmp[i]; while(tmp[i+1]==tmp[i]&&i<=n) ++i; } for(R int i=1;i<=n;++i) s[i]=lower_bound(tmp+1,tmp+1+tot,s[i])-tmp; for(R int i=1;i<=m;++i){ q[i].l=rd(); q[i].r=rd(); q[i].id=i; } sort(q+1,q+1+m,cmp1); bkt[s[1]]=cnt[1]=ans=1; int nowl=1,nowr=1; for(R int i=1;i<=m;++i){ while(nowl<q[i].l){del(nowl);++nowl;} while(nowl>q[i].l){--nowl;add(nowl);} while(nowr>q[i].r){del(nowr);--nowr;} while(nowr<q[i].r){++nowr;add(nowr);} q[i].ans=ans; } sort(q+1,q+1+m,cmp2); for(R int i=1;i<=m;++i) printf("%d\n",-q[i].ans); return 0; }