题意根据题目中给的冒泡排序写出每个元素交换过程中该元素位置左右最大差距;
分析:因为题目中冒泡程序从后向前遍历的,假设第i个元素左边有k个比i小的数,那么i必定会向右移动k位,我们用k1记住i+k,用k2记住i最终移到的位置a[i],用k3记住i的初始位置i,那么左右的最大值和最小值一定在k1,k2,k3中产生,此处不做证明,自己可以仔细想想。
现在主要问题在于寻找第i位置的左区间有几个比a[i]大的就可以了,解决办法:用线段树的节点储存次在节点左右区间的a[i]的个数,初始化为0,从a[n]到a[1]查询(1,a[i]-1)区间的数的个数,在把a[i]扔到线段树中更新就可以了。
代码如下:
#include <iostream> #include <stdio.h> #include <math.h> #include <map> #include <string.h> #include <algorithm> #define LL long long using namespace std; const int N=100010; int g[4*N]; void creat(int k,int l,int r) { if(l==r) { g[k]=0; return ; } int mid=(l+r)/2; creat(k<<1,l,mid); creat(k<<1|1,mid+1,r); g[k]=0; } int finds(int k,int l,int r,int L,int R) { if(l>=L&&r<=R) { return g[k]; } int mid=(l+r)/2; int k1=0,k2=0; if(mid>=L) { k1=finds(k<<1,l,mid,L,R); } if(mid<R) { k2=finds(k<<1|1,mid+1,r,L,R); } return k1+k2; } void updata(int k,int l,int r,int x) { if(l==r&&l==x) { g[k]=1; return ; } int mid=(l+r)/2; if(x<=mid) { updata(k<<1,l,mid,x); g[k]+=1; } if(x>mid) { updata(k<<1|1,mid+1,r,x); g[k]+=1; } return ; } int main() { int t,n,i,j,h=1,b[N],a[N],c[N]; cin>>t; while(t--) { scanf("%d",&n); for(i=1;i<=n;i++) { scanf("%d",&a[i]); } creat(1,1,n); for(i=n;i>0;i--) { if(a[i]==1) b[i]=0; else b[i]=finds(1,1,n,1,a[i]-1); updata(1,1,n,a[i]); } printf("Case #%d: ",h++); for(i=1;i<=n;i++) { int k1=b[i]+i; int k2=i; int k3=a[i]; c[a[i]]=max(max(k1,k2),k3)-min(min(k1,k2),k3); } for(i=1;i<=n;i++) { if(i==n) printf("%d",c[i]); else printf("%d ",c[i]); } printf(" "); } return 0; }