SS241115C. 排序(sort)
題意
給你一個長度為 \(n\) 的序列 \(a\),每次操作對 \([1,\frac{n}{2}],[\frac{n}{2}+1,n]\) 進行歸併排序。有 \(q\) 次詢問,給出 \(t,x\),問進行 \(t\) 次操作後 \(a_x\) 的值。
思路
考慮一次操作發生了什麼。
假設 \(x<y\),那麼 \(x\) 和它後面的一坨都會排到 \(y\) 前面。
這啟發我們把左右兩個序列分成若干區間,滿足每個區間的開頭都比後面那一坨大,比下一個區間開頭小。
把一個區間看做一個關於開頭的整體,一次操作就是給這些區間排序,區間內部不變。
進行一次操作後,如果有一個塊橫跨了左右不分,就把這個塊拆開,\(\le \frac{n}{2}\) 的為一部分,右邊拆成若干塊。
這個拆塊的操作我們只會做 \(O(n)\) 次,因為拆完就不會合並回去了。
可以使用權值線段樹維護。線段樹下標表示開頭的值,每次線段樹二分找出橫跨的塊,如果沒有那麼排序就結束了,否則拆塊,更新線段樹。
因為拆塊 \(O(n)\) 次,因此其實排序的操作最多也是 \(O(n)\) 次。
需要預處理每個位置後面第一個比它大的位置,這樣才能保證拆一次塊時間是 \(O(1)\) 的。
總時間複雜度 \(O(n \log n)\)。
code
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace struggle {
#define isdigit(x) (x>='0'&&x<='9')
#define gc getchar_unlocked
#define pc putchar_unlocked
template <typename T>
void read(T &x) {
x=0;
char ch=gc();
for(;!isdigit(ch);ch=gc()) ;
for(;isdigit(ch);ch=gc()) x=(x<<3)+(x<<1)+(ch^48);
}
template <typename T>
void write(T x,char ch) {
static int st[40];
int top=0;
do {
st[top++]=x%10;
x/=10;
}while(x);
while(top) pc(st[--top]^48);
pc(ch);
}
constexpr int N=2e5+7,M=1e6+7;
int n,q,t;
int a[N];
int c[N];
int x;
struct node {
int id,t,x;
}b[M];
bool cmp (node a,node b) { return a.t < b.t; }
int ne[N];
int que[N],r;
int _upper_bound(int x) {
if(a[que[1]]<x) return n+1;
int L=1,R=r;
while(L<R) {
int mid=(L+R+1)>>1;
if(a[que[mid]]>x) L=mid;
else R=mid-1;
}
return que[L];
}
int ans[M];
struct segtree {
int tr[N<<2];
void pushup(int u) { tr[u]=tr[u<<1]+tr[u<<1|1]; }
void insert(int u,int l,int r,int x,int w) {
if(l==r) {
return tr[u]+=w, void(0);
}
int mid=(l+r)>>1;
if(x<=mid) insert(u<<1,l,mid,x,w);
else insert(u<<1|1,mid+1,r,x,w);
pushup(u);
}
bool chai(int u,int l,int r,int k) {
if(l==r) {
if(tr[u]==k) return 0;
int len=tr[u]-k;
tr[u]=k;
int y=c[l]+k;
while(len) {
int p=min(len,ne[y]-y);
insert(1,1,n,a[y],p);
len-=p;
y+=p;
}
return 1;
}
int mid=(l+r)>>1;
bool ans=0;
if(tr[u<<1]>=k) ans=chai(u<<1,l,mid,k);
else ans=chai(u<<1|1,mid+1,r,k-tr[u<<1]);
pushup(u);
return ans;
}
int query(int u,int l,int r,int x) {
if(l==r) {
int p=c[l]+x-1;
return a[p];
}
int mid=(l+r)>>1;
if(tr[u<<1]>=x) return query(u<<1,l,mid,x);
return query(u<<1|1,mid+1,r,x-tr[u<<1]);
}
}T;
void main() {
read(n),read(q);
rep(i,1,n) read(a[i]), c[a[i]]=i;
rep(i,1,q) {
read(t),read(x);
b[i]={i,t-1,x};
}
sort(b+1,b+q+1,cmp);
per(i,n,1) {
ne[i]=_upper_bound(a[i]);
while(r&&a[que[r]]<a[i]) --r;
que[++r]=i;
}
int k=1;
while(k<=(n>>1)) {
int p=min(ne[k],(n>>1)+1);
T.insert(1,1,n,a[k],p-k);
k=p;
}
while(k<=n) {
int p=ne[k];
T.insert(1,1,n,a[k],p-k);
k=p;
}
int cnt=0;
int m=0;
while(m<q&&b[m+1].t==-1) ++m, ans[b[m].id]=a[b[m].x];
while(m<q&&b[m+1].t==0) ++m, ans[b[m].id]=T.query(1,1,n,b[m].x);
while(m<q) {
++cnt;
if(!T.chai(1,1,n,n>>1)) {
while(m<q) {
++m;
ans[b[m].id]=T.query(1,1,n,b[m].x);
}
break;
}
while(b[m+1].t==cnt) ++m, ans[b[m].id]=T.query(1,1,n,b[m].x);
}
rep(i,1,q) write(ans[i],'\n');
}
}
int main() {
#ifdef LOCAL
freopen("my.out","w",stdout);
#else
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
#endif
struggle :: main();
}