SS241115C. 排序(sort)

liyixin發表於2024-11-15

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();
}

相關文章