【loj 2736】「JOISC 2016 Day 3」回轉壽司【分塊】
阿新 • • 發佈:2021-01-08
Solution
首先考慮如果所有區間都是\([1,n]\),那麼每次我們只需要知道全域性的最大值就行了,只需要維護一個大根堆,並不關心內部的數字的順序。
考慮分塊,然後對整塊做出類似的處理
對每一個塊用一個大根堆維護權值,那麼在詢問時遇到整塊直接取出最大值,如果最大值大於\(A\)就將最大值取出來作為新的\(A\),將\(A\)push進大根堆,同時打一個標記。
對於散塊,我們希望能夠根據塊上的標記,直接暴力復原它的真是面貌:
考慮從左到右直接掃一遍,對於\(a_i\),如果目前所有標記中最小的一個都比\(a_i\)大,那麼\(a_i\)就可以保留,否則就將\(a_i\)替換為最小的標記,將\(a_i\)
因為標記的順序時互不影響的,所以我們用一個小根堆維護標記即可
總複雜度為\(\mathcal O(Q\sqrt{n}log(n))\),注意暴力跑散塊時要修改維護的大根堆
Code
#include<bits/stdc++.h> using namespace std; const int N=4e5+10; const int S=650; int n,q,a[N],bel[N],L[N],R[N],bl,k; priority_queue<int,vector<int>,greater<int> > p[S]; priority_queue<int> v[S]; inline void work(int x){ if(p[x].empty()) return; while(!v[x].empty()) v[x].pop(); for(int i=L[x];i<=R[x];++i){ if(p[x].top()<a[i]){ int u=p[x].top(); p[x].pop(); p[x].push(a[i]); a[i]=u; } v[x].push(a[i]); } while(!p[x].empty()) p[x].pop(); } inline void readd(int x){ while(!v[x].empty()) v[x].pop(); for(int i=L[x];i<=R[x];++i) v[x].push(a[i]); } inline int solve(int l,int r,int A){ int fl=bel[l],fr=bel[r]; if(fl==fr){ work(fl); for(int j=l;j<=r;++j) if(a[j]>A) swap(a[j],A); readd(fl); } else{ work(fl); for(int j=l;j<=R[fl];++j) if(a[j]>A) swap(a[j],A); readd(fl); for(int j=fl+1;j<fr;++j) if(v[j].top()>A){ p[j].push(A);v[j].push(A); A=v[j].top();v[j].pop(); } work(fr); for(int j=L[fr];j<=r;++j) if(a[j]>A) swap(a[j],A); readd(fr); } return A; } int main(){ scanf("%d%d",&n,&q); bl=pow(n,0.5);k=n/bl+1; for(int i=1;i<=n;++i) scanf("%d",&a[i]),bel[i]=(i-1)/bl+1,v[bel[i]].push(a[i]); for(int i=1;i<=k;++i) L[i]=(i-1)*bl+1,R[i]=min(i*bl,n); for(int i=1,l,r,A;i<=q;++i){ scanf("%d%d%d",&l,&r,&A); if(l<=r) printf("%d\n",solve(l,r,A)); else{ A=solve(l,n,A); printf("%d\n",solve(1,r,A)); } } return 0; }