冒泡排序
Description
for i from 1 to n - 1
if a[i] > a[i + 1]:
swap(a[i], a[i + 1])
给你以上冒泡排序的伪代码,每次询问数字 \(x\) 在第 \(k\) 次排序后所在位置的下标。
Solution
做这种题最重要的就是找规律,先手玩一组数据:
\[\underline{5} \; 4 \; \underline{9} \; 8 \; 1 \; \underline{10} \; 2 \; \textcolor{red}{6} \; 7 \; 3
\]
\[\underline{4 \; 5 \; 8} \; 1 \; \underline{9} \; 2 \; \textcolor{red}{6} \; 7 \; 3 \; 10
\]
\[\underline{4 \; 5} \; 1 \; \underline{8} \; 2 \; \textcolor{red}{6} \; 7 \; 3 \; 9 \; 10
\]
\[\underline{4} \; 1 \; \underline{5} \; 2 \; \underline{\textcolor{red}{6} \; 7} \; 3 \; 8 \; 9 \; 10
\]
\[\underline{1 \; 4} \; 2 \; \underline{5 \; \textcolor{red}{6}} \; 3 \; 7 \; 8 \; 9 \; 10
\]
\[\underline{1 \; 2 \; 4 \; 5} \; 3 \; \textcolor{red}{6} \; 7 \; 8 \; 9 \; 10
\]
\[\underline{1 \; 2 \; 4} \; 3 \; 5 \; \textcolor{red}{6} \; 7 \; 8 \; 9 \; 10
\]
\[1 \; 2 \; 3 \; 4 \; 5 \; \textcolor{red}{6} \; 7 \; 8 \; 9 \; 10
\]
如上,以数字 \(\textcolor{red}{6}\) 为例,可以观察到:
- 所有的数字都是先往左以直线的形式移动,到某个时间节点后向右跳步移到正确的位置。
再看到下划线的数,它们是数列的前缀最大值,发现:
- 数列的前缀最大值每次总是移动到上一次第一个比它大的数的位置的前一位。
然后做法就出来了,一种做法是利用结论二,记录每一个前缀最大值的位置,但是我不太会写。
我的做法是利用结论一,再稍加观察得到:
- 一个数前移的位数是它前面比他大的数的个数 \(k\)。
证明也很显然,因为前缀最大值每次向后移动一个,它前面刚好有 \(k\) 个前缀最大值。
那么后面的移动规律呢?看起来毫无章法,前缀最大值的移动也过于复杂,难以维护。
那我们就回到根本上来,数字移动的根本原因是什么呢?
以数字 \(\textcolor{red}{6}\) 为例,他最后为什么从位置 \(5\) 移到了位置 \(6\)?因为本来在它后面的数字 \(3\) 移动到了它前面!
那么在进行了 \(k\) 次操作后,数字 \(x\) 所在的位置就是在它前面比它小的数的个数加一,也就是在它前面的数的个数加一。
这不是废话?在它后面而且比它小的数在时间 \(k\) 的位置虽然不能确定(因为它也许移到前面后会再次后移),但是可以知道这种数有没有移到它的前面(因为小数移到前面后不会再移到大数后面)。
然后正解就出来了,建一棵值域主席树,维护前 \(k\) 小的数的位置,每次询问的时候,二分数字 \(x\) 所在的位置。
可以在线,时间复杂度 \(O(n \log^2 n)\),写主席树上二分可以做到 \(O(n \log n)\)。
点击查看代码
#include<bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define rep(i,l,r) for(int i=(l); i<=(r); ++i)
#define drep(i,r,l) for(int i=(r); i>=(l); --i)
using namespace std;
const int N=5e5+5;
int n,Q,k,a[N],x[N],p[N],c[N],ans[N];
vector<int>vec[N];
int t[N];
inline void add(int x) {
for(; x<N; x+=x&-x) ++t[x];
}
inline int query(int x,int r=0) {
for(; x; x-=x&-x) r+=t[x];
return r;
}
int tot=1,rt[N],ls[N<<5],rs[N<<5],sum[N<<5];
inline int modify(int pre,int l,int r,int x) {
int p=++tot,mid=(l+r)>>1;
sum[p]=sum[pre]+1,ls[p]=ls[pre],rs[p]=rs[pre];
if(l==r) return p;
if(x<=mid) ls[p]=modify(ls[pre],l,mid,x);
else rs[p]=modify(rs[pre],mid+1,r,x);
return p;
}
inline int query(int p,int l,int r,int ql,int qr) {
if(!p||ql>r||qr<l) return 0;
if(ql<=l&&r<=qr) return sum[p];
int mid=(l+r)>>1;
return query(ls[p],l,mid,ql,qr)+query(rs[p],mid+1,r,ql,qr);
}
inline bool chk(int mid,int i,int id) {
return query(rt[x[id]-1],1,n,1,mid+i)<mid;
}
signed main() {
FASTIO;
// freopen("bubble.in","r",stdin);
// freopen("bubble.out","w",stdout);
cin>>n;
rep(i,1,n) {
cin>>a[i],p[a[i]]=i;
c[a[i]]=query(n-a[i]+1),add(n-a[i]+1);
}
rep(i,1,n) rt[i]=modify(rt[i-1],1,n,p[i]);
cin>>Q;
rep(i,1,Q) cin>>k>>x[i],vec[k].push_back(i);
memset(t,0,sizeof t);
rep(i,1,n) {
for(auto id:vec[i]) {
if(i<=c[x[id]]) ans[id]=p[x[id]]-i;
else {
int l=1,r=n,mid;
while(l<r) mid=(l+r)>>1,chk(mid,i,id)?r=mid:l=mid+1;
ans[id]=l;
}
}
}
rep(i,1,Q) cout<<ans[i]<<'\n';
}
浙公网安备 33010602011771号