P8996 [CEOI2022] Abracadabra
首先你发现归并的本质就是每轮选当前两段数中较小的那个数
再考虑一轮洗牌会对序列产生什么影响。容易发现,如果第
那我们暴力维护这个过程每次需要的操作就是找到第
我们可以考虑用权值树状数组来维护这个过程,每个位置记录以
总时间复杂度
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define lowbit(x) (x&(-x))
using namespace std;
const int N=200005;
const int M=1000005;
int n,Q,top,a[N],nxt[N],pos[N],stc[N],c[N],ans[M]; vector<pii>q[N];
inline void update(int x,int y){ while(x<=n) c[x]+=y,x+=lowbit(x); }
inline int query(int x){ int r=0; while(x) r+=c[x],x-=lowbit(x); return r; }
inline pii find(int x){
int pos=0,sum=0;
for(int i=17;~i;i--)
if((pos|(1<<i))<=n&&sum+c[pos|(1<<i)]<x)
sum+=c[pos|(1<<i)],pos|=1<<i;
return mp(pos+1,x-sum);
}
int main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>n>>Q;
for(int i=1;i<=n;i++) cin>>a[i],pos[a[i]]=i; stc[0]=n+1;
for(int i=n;i;i--){
while(top&&a[i]>a[stc[top]]) top--;
nxt[i]=stc[top]; stc[++top]=i;
}
for(int i=1;i<=n;i=nxt[i]) update(a[i],nxt[i]-i);
for(int i=1,t,k;i<=Q;i++){
cin>>t>>k; t=min(t,n);
q[t].emplace_back(i,k);
}
for(int i=0;i<=n;i++){
for(pii j:q[i]){
pii o=find(j.second);
ans[j.first]=a[pos[o.first]+o.second-1];
}
int mid=n/2+1; pii k=find(mid);
if(k.second==1) continue;
int len=query(k.first)-query(k.first-1);
update(k.first,k.second-1-len);
for(int j=pos[k.first]+k.second-1;j<=pos[k.first]+len-1;j=nxt[j])
update(a[j],min(pos[k.first]+len,nxt[j])-j);
}
for(int i=1;i<=Q;i++) cout<<ans[i]<<'\n';
return 0;
}