[JOI Open 2025] 冒泡排序机 / Bubble Sort Machine
[JOI Open 2025] 冒泡排序机 / Bubble Sort Machine。
经验:[ICPC 2023 Seoul R] Apricot Seeds。
给定长度 n 的数组 a,求冒泡排序第 K 轮后区间 [l,r] 的和。
n≤5×105。
首先看到区间 [l,r],先转化为前缀和 [1,x]。
然后考虑刻画冒泡排序的结果。
先看看冒泡排序第一轮的结果 a′。那么对于第 1 个位置,显然排序后其值为 min{a1,a2}。
然后是第二个位置。因为 a1′=min{a1,a2},所以交换第一个数后 a2′=max{a1,a2};然后又因为第二个数会和第三个数交换,于是 a2′=min{a3,max{a1,a2}}。
然后对于 a3,由于冒泡排序的性质,最大的数总是在最后;因此交换前两个后 a3′=max{a1,a2,a3},然后交换第 3,4 个,可得 a3′=min{a4,max{a1,a2,a3}}。
由此,归纳可证:
引理 1:
冒泡排序第一轮的结果 a′ 满足:
ai′=min{ai+1,1≤j≤imax{aj}}
考虑扩展这一想法。
显然对于 a1,冒泡排序第 K 轮的结果 a′ 满足 a1′=min{aj∣1≤j≤K+1}。
然后,对于 a2,第一轮 a′ 他会变为 min{a3,max{a1,a2}},第二轮 a′′ 他就会迭代的变为:
a2′′=min{a3′,max{a1′,a2′}}=min{min{a4,max{a1,a2,a3}},max{min{a1,a2},min{a3,max{a1,a2}}}}
归纳的,可证:
引理 2:
冒泡排序第 K 轮的结果 a(K) 满足前 x 项为原序列的前 x+K 个数中前 x 大的。
因此只需要可持久化线段树或者 Wavelet 就可以解决。
PS:如果还是看不懂引理 2 的归纳证明,可以简单理解为:K 轮时前 x 个数只会和前 x+K 个数发生交互,然后由于冒泡排序的性质,后 K 大的数会被弹出前 x 个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| #include <bits/stdc++.h> using namespace std; const int N=500100,M=20000010; using ll=long long; int ls[M],rs[M],num[M],n,m,a[N],am[N],ss,rt[N],idx; ll sum[M]; int build(int p,int l,int r){ p=++idx; if(l==r)return p; int mid=(l+r)>>1; ls[p]=build(p,l,mid); rs[p]=build(p,mid+1,r); return p; } void copy(int x,int y){ls[x]=ls[y],rs[x]=rs[y],num[x]=num[y],sum[x]=sum[y];} int insert(int p,int l,int r,int x){ ++idx,copy(idx,p),p=idx; if(l==r){ num[p]++; sum[p]+=am[x]; return p; } int mid=(l+r)>>1; if(mid>=x)ls[p]=insert(ls[p],l,mid,x); else rs[p]=insert(rs[p],mid+1,r,x); num[p]=num[ls[p]]+num[rs[p]]; sum[p]=sum[ls[p]]+sum[rs[p]]; return p; } ll query(int p,int l,int r,int x){ if(l==r)return 1ll*am[l]*x; int mid=(l+r)>>1,k=num[ls[p]]; if(k>=x)return query(ls[p],l,mid,x); return query(rs[p],mid+1,r,x-k)+sum[ls[p]]; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",a+i),am[++ss]=a[i]; sort(am+1,am+1+ss);ss=unique(am+1,am+1+ss)-am-1; for(int i=1;i<=n;i++)a[i]=lower_bound(am+1,am+1+ss,a[i])-am; rt[0]=build(1,1,ss); for(int i=1;i<=n;i++) rt[i]=insert(rt[i-1],1,ss,a[i]); scanf("%d",&m); int K=0; while(m--){ int opt,x,y; scanf("%d",&opt); if(opt==1)K++; else{ scanf("%d%d",&x,&y); printf("%lld\n",-(x==0?0:query(rt[min(n,x-1+K)],1,ss,x-1))+ query(rt[min(n,y+K)],1,ss,y)); } } return 0; }
|