题解:
考虑一个位置上的数字c在冒泡排序过程的变化情况。c会被其后面比c小的数字各交换一次,之后c就会只向前移动。数组从右向左扫,树状数组维护一下得到每个值右边有多少个比其小的值,加上原位置得到最右位置,最左位置为初始位置和最终位置的最小值。(我用的是划分树- -)
ACcode:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100100
using namespace std;
int tree[20][maxn];
int sorted[maxn];
int toleft[20][maxn];
int a[maxn];
void build(int l,int r,int dep){
if(l==r)return ;
int mid=(l+r)>>1;
int same=mid-l+1;
for(int i=l;i<=r;++i)
if(tree[dep][i]<sorted[mid])
same--;
int lpos=l;
int rpos=mid+1;
for(int i=l;i<=r;++i){
if(tree[dep][i]<sorted[mid])
tree[dep+1][lpos++]=tree[dep][i];
else if(tree[dep][i]==sorted[mid]&&same>0){
tree[dep+1][lpos++]=tree[dep][i];
same--;
}
else
tree[dep+1][rpos++]=tree[dep][i];
toleft[dep][i]=toleft[dep][l-1]+lpos-l;
}
build(l,mid,dep+1);
build(mid+1,r,dep+1);
}
int query(int L,int R,int l,int r,int dep,int k){
if(l==r)return tree[dep][l];
int mid=(L+R)>>1;
int cnt=toleft[dep][r]-toleft[dep][l-1];
if(cnt>=k){
int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
int newr=newl+cnt-1;
return query(L,mid,newl,newr,dep+1,k);
}
else{
int newr =r+toleft[dep][R]-toleft[dep][r];
int newl=newr-(r-l-cnt);
return query(mid+1,R,newl,newr,dep+1,k-cnt);
}
}
int main(){
int n,q,cnt=1,loop;
scanf("%d",&loop);
while(loop--){
scanf("%d",&n);
memset(tree,0,sizeof(tree));
for(int i=1;i<=n;++i){
scanf("%d",&tree[0][i]);
sorted[i]=tree[0][i];
a[sorted[i]]=i;
}
sort(sorted+1,sorted+1+n);
build(1,n,0);
// for(int i=1;i<=n;++i)cout<<a[i]<<" ";cout<<'\12';
printf("Case #%d:",cnt++);
for(int i=1;i<=n;++i){
int ans;
int l=1,r=n-a[i]+1;
while(l<=r){
int mid=(l+r)>>1;
int tmp=query(1,n,a[i],n,0,mid);
if(tmp==i){
ans=mid;
break;
}
if(tmp>i)r=mid-1;
else l=mid+1;
}
cout<<" "<<ans-1+a[i]-min(a[i],i);
}
cout<<'\12';
}
return 0;
}