題解 P5251 【[LnOI2019]第二代圖靈機】
阿新 • • 發佈:2020-09-15
轉載註明來源:https://www.cnblogs.com/syc233/p/13673494.html
珂朵莉樹+尺取法+線段樹。
大體思路是珂朵莉樹維護顏色段,線段樹維護區間和、區間最值,3、4操作在珂朵莉樹上做尺取法。
主要說一下尺取法的細節:
操作3
詢問區間 \([l,r]\) 中包含所有(一共 \(c\) 種)顏色,數字和最小的子區間的數字和。
先將詢問區間在珂朵莉樹上取出來,固定區間左端點,移動右端點,用桶維護區間內每種顏色的出現次數和區間顏色總數。
當區間包含所有顏色時停止移動右端點,即算出包含所有顏色的最短區間。對於左右端點的位置進行分類討論:
- 若左右端點同屬一個塊,即 \(c=1\)
- 否則,令左端點在塊 \([l1,r1]\) 中,右端點在 \([l2,r2]\) 中,則取區間 \([r1,l2]\) 。
inline int query1(int l,int r) { IT itr=split(r+1),itl=split(l); memset(sta,0,sizeof(sta));cnt=0; int ans=INF; for(IT l=itl,r=itl;l!=itr;++l) { while(r!=itr&&cnt!=c) Add(r->val,r->r-r->l+1),++r; --r; if(cnt==c) { if(l==r) ans=min(ans,st.query_min(1,l->l,l->r)); else ans=min(ans,st.query_sum(1,l->r,r->l)); } ++r; Del(l->val,l->r-l->l+1); } return ans==INF?-1:ans; }
操作4
表示詢問區間 \([l,r]\) 中沒有重複顏色,數字和最大的子區間的數字和。
首先只取一個數顯然是可行的,那麼 \(ans\) 的初值即為區間最大值。
然後類似操作3,將區間從珂朵莉樹上取出來,做尺取法。
因為只取一個數的情況已經處理,所以尺取時左右端點不能在同一個塊中。要保證區間中沒有重複顏色,那麼每次擴充套件右端點時只能加入大小等於 \(1\) 的塊 。然而合法區間的右端點是可能在一個大小大於 \(1\) 的塊中的,因為可以只取這個塊的第一個數,在每次擴充套件完後臨時加入這種塊即可。
inline int query2(int l,int r) { IT itr=split(r+1),itl=split(l); memset(sta,0,sizeof(sta));cnt=0; int ans=st.query_max(1,l,r); for(IT l=itl,r=itl;l!=itr;++l) { if(l==r) Add(r->val,1),++r; while(r!=itr&&!sta[r->val]&&r->r-r->l+1==1) Add(r->val,1),++r; bool flag=false; if(r!=itr&&!sta[r->val]) Add(r->val,1),++r,flag=true; --r; if(l!=r) ans=max(ans,st.query_sum(1,l->r,r->l)); Del(l->val,1); if(flag) Del(r->val,1),--r;// 將臨時加入的塊刪除 ++r; } return ans; }
完整程式碼就沒必要放上來了吧,相信來做這道題的人都會珂朵莉樹和線段樹。