【題解】「Ynoi2018」未來日記 [*medium]
阿新 • • 發佈:2020-12-31
查詢 \(kth\) 的話,就分塊來講,通常可以考慮值域分塊。
具體操作就是將值域分塊,然後詢問的時候先確定塊,再確定目標位置。
令 \(sum_{i,j}\) 表示前 \(i\) 個序列塊,第 \(j\) 個數值塊的出現次數;\(cnt_{i,j}\) 表示前 \(i\) 個序列塊,第 \(j\) 個數的出現次數。
這樣話查詢的時候拿個桶記錄一下散塊貢獻即可。
修改的話其實就是從左端點的塊一直修改到最後面的塊,更新 \(sum,cnt\) 即可。
這下子就有一個問題:當處理散塊時,需要直接訪問 \(a_i\) 。這就意味著 \(a_i\) 必須支援實時查詢。怎麼維護 \(a_i\) 呢?這個時候我們可以想到第二分塊中用並查集維護 "將所有 \(x\)
關於實現:
- 程式碼量不算很大,也不怎麼卡常,細節也不是很多。(總之很好寫
- 注意到 \(c\) 陣列其實是一個 \(n\sqrt{n}\) 大小的陣列:
- 如果第一維開 \(n\) ,第二維開 \(\sqrt{n}\) :修改的時候記憶體訪問所佔常數太大。
- 如果第一維開 \(\sqrt{n}\) ,第二維開 \(n\) :這樣的話記憶體訪問就舒服不少。實測快了不少。
- 並查集的
merge
:如果沒有 \(x\) 這個顏色就跳過。實測快了不少。(卡了這個點就過了,之前一直是 \(64\ pts\) - 如果硬說細節的話:更新 \(sum,cnt\)
吐槽:感覺這題比第二分塊簡單一些(
const int N=1e5+5; const int S=5e2+5; const int M=1e5; int n,m,x,y,a[N]; // {{{ Data_Struct_Block int sqrtn,L[S],R[S],id[N],mi[S],mx[S],blo[N]; int sum[S][S],cnt[N][S],tmp_sum[S],tmp_cnt[N]; // {{{ Data_Struct_Dsu int rt[S][N],col[N],fa[N]; int find(int u) {return fa[u]==u?u:fa[u]=find(fa[u]);} inline void merge(const int &t) { if(!rt[t][x]) return ; if(!rt[t][y]) rt[t][y]=rt[t][x],col[rt[t][y]]=y; else fa[rt[t][x]]=rt[t][y]; rt[t][x]=0; } inline void rebuild(const int &t,int l,int r) { lep(i,L[t],R[t]) a[i]=col[find(i)],rt[t][a[i]]=0; lep(i,l,r) if(a[i]==x) a[i]=y; lep(i,L[t],R[t]) { if(!rt[t][a[i]]) col[rt[t][a[i]]=i]=a[i]; fa[i]=rt[t][a[i]]; } } // }}} inline void init() { sqrtn=sqrt(n*3/5+1); for(int i=1,c=1,j;i<=n;i=j+1,++c) { // seq_block L[c]=i,R[c]=j=min(n,i+sqrtn-1); lep(t,L[c],R[c]) id[t]=c; } for(int i=1,c=1,j;i<=M;i=j+1,++c) { // num_block mi[c]=i,mx[c]=j=min(M,i+315); lep(t,mi[c],mx[c]) blo[t]=c; } lep(c,1,id[n]) { // cnt & sum COPY(sum[c],sum[c-1]); lep(i,1,M) cnt[i][c]=cnt[i][c-1]; lep(i,L[c],R[c]) ++cnt[a[i]][c],++sum[c][blo[a[i]]]; } lep(c,1,id[n]) lep(i,L[c],R[c]) { // dsu if(!rt[c][a[i]]) col[rt[c][a[i]]=i]=a[i]; fa[i]=rt[c][a[i]]; } } inline void modify(int l,int r) { if(x==y) return ; int res=0,tmp=0,v1=0,v2=0; #define upd1 (sum[i][blo[x]]-=res,sum[i][blo[y]]+=res) #define upd2 (cnt[x][i]-=res,cnt[y][i]+=res) if(id[l]==id[r]) { lep(i,l,r) if(col[find(i)]==x) ++res; if(!res) return ; lep(i,id[r],id[n]) upd1,upd2; rebuild(id[l],l,r); } else { lep(i,l,R[id[l]]) if(col[find(i)]==x) ++res,++v1; lep(i,id[l],id[r]-1) res+=tmp,upd1,tmp=cnt[x][i+1]-cnt[x][i],upd2; lep(i,L[id[r]],r) if(col[find(i)]==x) ++res,++v2; lep(i,id[r],id[n]) upd1,upd2; lep(i,id[l]+1,id[r]-1) merge(i); if(v1) rebuild(id[l],l,R[id[l]]); if(v2) rebuild(id[r],L[id[r]],r); } } inline void query(int l,int r) { int tmp; const bool _=(id[l]!=id[r]); #define add (tmp=col[find(i)],++tmp_cnt[tmp],++tmp_sum[blo[tmp]]) #define del (tmp=col[find(i)],--tmp_cnt[tmp],--tmp_sum[blo[tmp]]) if(_) {lep(i,l,R[id[l]]) add; lep(i,L[id[r]],r) add;} else lep(i,l,r) add; int res=0,ans=-1; lep(t,1,blo[M]) { int now=tmp_sum[t]+sum[id[r]-_][t]-sum[id[l]][t]; if(res+now>=x) lep(i,mi[t],mx[t]) { res+=tmp_cnt[i]+cnt[i][id[r]-_]-cnt[i][id[l]]; if(res>=x) {ans=i; break;} } if(~ans) break; res+=now; } printf("%d\n",ans); if(_) {lep(i,l,R[id[l]]) del; lep(i,L[id[r]],r) del;} else lep(i,l,r) del; } // }}} int op,l,r; int main() { IN(n,m); lep(i,1,n) IN(a[i]); init(); while(m--) { IN(op,l,r); if(op==1) IN(x,y),modify(l,r); if(op==2) IN(x),query(l,r); } return 0; }