[JOI Open 2025] 冒泡排序机 / Bubble Sort Machine

[JOI Open 2025] 冒泡排序机 / Bubble Sort Machine

考虑刻画冒泡排序的过程。

我们不妨手玩几个样例,比如对于序列 \([2,0,2,6,0,4,0,1]\),其进行冒泡排序的结果:

  • \(0\) 轮:\([2,0,2,6,0,4,0,1]\)。

  • \(1\) 轮:\([0,2,2,0,4,0,1,6]\)。

  • \(2\) 轮:\([0,2,0,2,0,1,4,6]\)。

  • \(3\) 轮:\([0,0,2,0,1,2,4,6]\)。

  • \(4\) 轮:\([0,0,0,1,2,2,4,6]\)。

后面的轮次都是 \([0,0,0,1,2,2,4,6]\)。

我们猜测经过 \(x\) 轮排序后的前 \(y\) 个数,是原序列的前 \(x+y\) 个数中前 \(y\) 小的。

结论根据冒泡排序的性质是显然的,具体考虑前 \(y\) 个数在一轮排序后会移除最大的一个。

将区间和转化为前缀和,问题变为求前缀前 \(k\) 小之和。

时空复杂度 \(O(n \log n)\)。

//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[500010],id[500010],num[500010];
bool cmp(const int &lhs,const int &rhs){
	return a[lhs]<a[rhs]; 
}
int root[500010];
int lc[20000010],rc[20000010],sum_cnt[20000010],tot;
long long sum_num[20000010];
void pushup(int id){
	sum_cnt[id]=sum_cnt[lc[id]]+sum_cnt[rc[id]];
	sum_num[id]=sum_num[lc[id]]+sum_num[rc[id]];
}
void build_tree(int &id,int l,int r){
	id=++tot;
	if(l<r){
		int mid=(l+r)>>1;
		build_tree(lc[id],l,mid);
		build_tree(rc[id],mid+1,r);
	}
}
void add_tree(int id1,int &id2,int l,int r,int pos){
	id2=++tot;
	if(l==r){
		sum_cnt[id2]=1;
		sum_num[id2]=num[pos]; 
	}
	else{
		int mid=(l+r)>>1;
		if(pos<=mid){
			rc[id2]=rc[id1];
			add_tree(lc[id1],lc[id2],l,mid,pos);
		} 
		else{
			lc[id2]=lc[id1];
			add_tree(rc[id1],rc[id2],mid+1,r,pos);
		}
		pushup(id2);
	}
}
int n,q;
long long query(int id,int l,int r,int k){
	if(l==r){
		return sum_num[id];
	}
	int mid=(l+r)>>1;
	if(sum_cnt[lc[id]]>=k){
		return query(lc[id],l,mid,k);
	}
	else{
		return sum_num[lc[id]]+query(rc[id],mid+1,r,k-sum_cnt[lc[id]]);
	}
}
long long qry(int rnd,int id){
	return query(root[min(n,id+rnd)],1,n,id);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		id[i]=i;
	}
	sort(id+1,id+n+1,cmp);
	for(int i=1;i<=n;i++){
		num[i]=a[id[i]];
		a[id[i]]=i;
	}
	build_tree(root[0],1,n);
	for(int i=1;i<=n;i++){
		add_tree(root[i-1],root[i],1,n,a[i]);
	}
	scanf("%d",&q);
	int pre_round=0;
	while(q--){
		int cmd;
		scanf("%d",&cmd);
		if(cmd==2){
			int l,r;
			scanf("%d %d",&l,&r);
			long long ans=qry(pre_round,r);
			if(l>1){
				ans-=qry(pre_round,l-1);
			}
			printf("%lld\n",ans);
		}
		else{
			pre_round++;
		}
	}
	return 0;
}
posted @ 2026-03-30 18:26  Oken喵~  阅读(21)  评论(0)    收藏  举报