7
2 6 5 7 4 1 3
2 6 5 7 1 4 3
2 6 5 1 7 4 3
2 6 1 5 7 4 3
2 1 6 5 7 4 3
1 2 6 5 7 4 3
1 2 6 5 7 3 4
1 2 6 5 3 7 4
1 2 6 3 5 7 4
1 2 3 6 5 7 4
1 2 3 6 5 4 7
1 2 3 6 4 5 7
1 2 3 4 6 5 7
1 2 3 4 5 6 7
打一下一个较长序列的运行过程,发现对于一个数,可以分为两类去看:
一种是类似7这个数,它的初始位置是4,整个过程中都是要往后移动的,7的后面有三个比它小的数字4,1,3,也就是说7在冒泡的过程中,最远向右偏移3个位置;
还有一种数字是类似于4这个数字,它初始位置是5,它要最终移动到位置4,最多左移 5 - 4 = 1个位置,4这个数字右面有两个比它小的数字1,3,也就是说在移动过程中向右最远移动2位,所以数字4的移动范围是1 + 2 = 3;
剩下的就用线段树来求一个数后面有多少比它小的数字,跟求逆序数很类似
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define maxn 100005
#define lson step<<1
#define rson step<<1|1
using namespace std;
int sum[maxn<<2];
int a[maxn],index[maxn];
int jl[maxn];
void pushup(int step)
{
sum[step]=sum[lson]+sum[rson];
}
void build(int l,int r,int step)
{
sum[step]=0;
if(l==r)
return ;
int mid=(l+r)>>1;
build(l,mid,lson);
build(mid+1,r,rson);
}
void update(int l,int r,int pos,int step)
{
if(l==r)
{
sum[step]++;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)
update(l,mid,pos,lson);
else
update(mid+1,r,pos,rson);
pushup(step);
}
int query(int left,int right,int l,int r,int step)
{
if(left==l&&r==right)
{
return sum[step];
}
int mid=(l+r)>>1;
int res=0;
if(right<=mid)
res+=query(left,right,l,mid,lson);
else if(left>mid)
res+=query(left,right,mid+1,r,rson);
else
{
res+=query(left,mid,l,mid,lson);
res+=query(mid+1,right,mid+1,r,rson);
}
return res;
}
int main()
{
int T;
scanf("%d",&T);
int cnt=1;
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
index[t]=i;
a[i]=t;
}
build(1,n,1);
for(int i=n;i>=1;i--)
{
jl[a[i]]=query(1,a[i],1,n,1);
update(1,n,a[i],1);
}
printf("Case #%d:",cnt++);
for(int i=1;i<=n;i++)
{
if(index[i]<=i)
printf(" %d",jl[i]);
else
printf(" %d",(jl[i]+index[i]-i));
}
puts("");
}
return 0;
}