冒泡排序二合一 题解
冒泡排序二合一 题解
知识点
线段树,树状数组,主席树。
题意简述
给定一个排列 \(a\),有下列两种询问:
- 给定 \(l,r,k,x\),求将 \(a\) 的区间 \([l,r]\) 冒泡排序 \(k\) 轮之后,\(x\) 这个数在数组中的位置。
- 给定 \(l,r,k,x\),求将 \(a\) 的区间 \([l,r]\) 冒泡排序 \(k\) 轮之后,\(x\) 这个位置的数是多少。
每个询问独立。
定义对 \(a\) 的区间 \([l,r]\) 进行一轮冒泡排序为依次对于 \(i=l,l+1,\dots,r-1\),若 \(a_i>a_{i+1}\),则交换 \(a_i,a_{i+1}\)。
分析
首先这明显是一道缝合题,要分成两部分求解。其次注意如查询的数不在 \([l,r]\) 内需要特判。
TYPE 1
这个部分比较简单。
首先很直观地可以感受到,在一轮排序中,一个数如果前面有大于它的数,结束后它会往前移动一个位置;否则它会移动到后面第一个大于它的值前面。
那么 \(k\) 轮其实是连续的过程,一个数肯定是先连续几次往前移动(可以是 \(0\) 次),然后再一直跑到第一个大于它的值前面,最后不动。
所以我们可以按询问中的 \(x\) 降序做扫描线,扫出在 \(x\) 之前有几个大于它,设为 \(s\)。
-
如果 \(k\le x\),其实就是往前移动 \(k\) 位。
-
如果 \(k>x\),除了先往前移动 \(s\) 位,还需要进一步考虑后面的影响:
发现当 \(x\) 第一次往后移动,它跳到了 \([l,r]\) 中第 \(s+1\) 个大于它的值,依次类推,第 \(k-s\) 次就是第 \(k\) 个大于它的值。除非没有这么多大于它的值,那么直接找到停止位置即可。
所以我们可以考虑找到 \([l,r]\) 中第 \(k\) 个大于 \(x\) 的值的位置,减去 \(k\) 即可。
可以用树状数组实现,也可以用线段树。
TYPE 2
这个部分稍难。
首先你要发现这个可以二分,那么就有 \(O(n\log^2{n})\) 的整体二分做法:
检查答案是否 \(\ge lim\),考虑把 \(\ge lim\) 的点都视作 \(1\),其余为 \(0\),我们只需要考虑排完序之后 \(x\) 这个位置的值是否为 \(1\)。那么很简单,排序 \(k\) 轮就是把前 \(k\) 个 \(1\) 放到序列最后,其余 \(1\) 往前移动 \(k\) 位(不足 \(k\) 个 \(1\) 就全部放到最后)。
现在考虑如何优化,思考二分过程,发现可以分为两部分:
-
\(x \ge r-k+1\),那么一定是在最后的 \(k\) 个位置里,区间第 \(k\) 大即可解决。
-
否则在二分时,\(x\) 处的值取决于 \(x+k\) 处的值,原因很简单:
- 当 \(x+k\) 前有 \(\ge k\) 个 \(1\) 时,它会向前移动 \(k\) 位。
- 当 \(x+k\) 前 \(1\) 的个数 \(<k\) 时,此时 \(x+k\) 之前的 \(1\) 全部会跑到序列最后,但是 \(x\) 处的值会取到原本在 \(<x+k\) 处的值,那么必定是 \(0\)。
那么只需考虑 \(x+k\) 前有 \(\ge k\) 个 \(1\) 的情况,求出 \(<x+k\) 的第 \(k\) 大值 \(s\),在 \(lim \le s\) 时都是满足这种情况的,只要再考虑 \(a_{x+k}\) 即可,答案即为 \(\min(a_{x+k},s)\)。
上述操作可以主席树实现。
代码
constexpr int N(6e5+10);
int n,Q,TYPE;
int a[N],p[N];
struct BIT {
int c[N];
void Plus(int x) { if(x>0)for(; x<=n; x+=x&-x)++c[x]; }
int Sum(int x) {
int ans(0);
if(x<=n)for(; x>0; x&=x-1)ans+=c[x];
return ans;
}
int Sum(int l,int r) { return l<=r?Sum(r)-Sum(l-1):0; }
int Double(int k) {
int ans(0),sum(0);
DOR(i,19,0)if((ans|1<<i)<=n&&sum+c[ans|1<<i]<k)sum+=c[ans|=1<<i];
return ans+(sum<k);
}
} bit;
namespace T1 {
int ans[N];
struct Query { int l,r,k,idx; };
vector<Query> Que[N];
int Main() {
/*DE("Offline");*/
FOR(i,1,Q) {
int l,r,k,x;
I(l,r,k,x);
if(p[x]<l||p[x]>r||!k) {
ans[i]=p[x];
continue;
}
Que[x].push_back({l,r,k,i});
}
/*DE("Solve");*/
DOR(x,n,1) {
bit.Plus(p[x]);
for(const Query &que:Que[x]) {
const int &l(que.l),&r(que.r),&k(que.k);
int lef(bit.Sum(l,p[x]-1));
if(k<=lef) {
ans[que.idx]=p[x]-k;
continue;
}
int ord(bit.Sum(l-1)+k+1);
int pos(min(r,bit.Double(ord)));
int rig((pos-p[x])-bit.Sum(p[x]+1,pos));
ans[que.idx]=p[x]-lef+rig;
}
}
/*DE("Output");*/
FOR(i,1,Q)O(ans[i],'\n');
return 0;
}
}
struct SEG {
int tot;
int rt[N];
struct node {
int ls,rs,sum;
node():ls(0),rs(0),sum(0) {}
} tr[N*21];
SEG():tot(0) {}
int &operator [](int i) { return rt[i]; }
#define ls(p) (tr[p].ls)
#define rs(p) (tr[p].rs)
#define mid ((l+r)>>1)
void Insert(int x,int &p,int q,int l=1,int r=n) {
tr[p=++tot]=tr[q],++tr[p].sum;
if(l==r)return;
return x<=mid?Insert(x,ls(p),ls(q),l,mid):Insert(x,rs(p),rs(q),mid+1,r);
}
int Kth(int k,int p,int q,int l=1,int r=n) {
if(l==r)return l;
int sum(tr[rs(q)].sum-tr[rs(p)].sum);
if(k<=sum)return Kth(k,rs(p),rs(q),mid+1,r);
return Kth(k-sum,ls(p),ls(q),l,mid);
}
#undef ls
#undef rs
#undef mid
} seg;
namespace T2 {
int Main() {
/*DE("Init");*/
FOR(i,1,n)seg.Insert(a[i],seg[i],seg[i-1]);
/*DE("Query");*/
while(Q--) {
int l,r,k,x;
I(l,r,k,x);
if(x<l||x>r||!k) {
O(a[x],'\n');
continue;
}
if(x>=r-k+1) {
O(seg.Kth(r-x+1,seg[l-1],seg[r]),'\n');
continue;
}
O(min(a[x+k],seg.Kth(k,seg[l-1],seg[x+k-1])),'\n');
}
return 0;
}
}
signed main() {
/*DE("Input");*/
I(n,Q,TYPE);
FOR(i,1,n)I(a[i]),p[a[i]]=i;
return TYPE==1?T1::Main():T2::Main();
}

浙公网安备 33010602011771号