官方题解:https://media.hihocoder.com/contests/challenge34/tutorials-previewed.pdf
题目链接:http://hihocoder.com/problemset/problem/1781
题意问对于给定序列A,是否存在一个整数k, 使得A冒泡k轮后变成序列B.
这题一种做法是像官方题解一样写个计算区间最值的数据结构。
而我是另一种做法,通过的逆序数 来判断A怎样能变化到B。
例子
首先我举一个例子:
对于序列 A
8 7 5 1 9 2 6 4 3
其每个位置的逆序数是:
0 1 2 3 0 4 3 5 6 (*)
接着对A冒泡一轮,得到:
7 5 1 8 2 6 4 3 9
这时每个位置的逆序数是:
0 1 2 0 3 2 4 5 0 (**)
那么序列(*)和序列(**) 直接有啥联系呢?
(**)=(*)每个数减-1,并向左平移一格 ,且最多减到0.
证明
现在来证明,每冒泡一轮 所有位置的逆序数-1,并向左移一格。
引理1:对于每轮冒泡排序,若一个位置的前面存在比他大的数, 则他一定会他前面的某个比他大的数进行有且仅有一次交换
这个引理大家自已脑补一下,应该很容易理解是对的。
引理2:若一个位置的前面存在比他大的数,则个位置的逆序数大于0
这个废话,我就不解释了。
证:因为任意一个逆序大于1位置,都与比他大的数交换一次,交换后首先逆序数必然减一,其次,交换后为位置肯定前移1格。 故结论成立。
解题思路
有了这个规律,显然我可以直接O(n)求出任意一轮A的逆序数 与B比较。
再利用【逆序数和】每轮的都会递减的单调性。就可以用二分比较逆序数和的方式,在O(n logn)时间定位b.
或者做一个O(n)预处理,算出n-1轮中,每轮的逆序数和,这样可以把查询的时间复杂度降低至O(n)
当然因为还要对数据进行O(n logn)离散化和求逆序数的原因,所以无论你写哪种最终复杂度都是O(n logn)。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<stdio.h> 2 #include<vector> 3 #include<algorithm> 4 #include<string.h> 5 #include<stack> 6 #include<math.h> 7 using namespace std; 8 #define lowbit(x) (x&(-x)) 9 int a[100005],b[100004]; 10 int s1[200005],s2[100005]; 11 int c[200005]; 12 int N=200005; 13 int cot[200005]; 14 int getsum(int x) 15 { 16 int sum=0; 17 while(x) 18 { 19 sum+=c[x]; 20 x-=lowbit(x); 21 } 22 return sum; 23 } 24 void add(int x) 25 { 26 while(x<=N) 27 { 28 c[x]++; 29 x+=lowbit(x); 30 } 31 } 32 vector<int>que; 33 int getid(int x) 34 { 35 return lower_bound(que.begin(),que.end(),x)-que.begin()+1; 36 } 37 void cal(int n,int a[],int ans[]) 38 { 39 int i; 40 memset(c,0,sizeof(c)); 41 for(i=1; i<=n; i++) 42 { 43 ans[i]=(i-1)-getsum(a[i]); 44 add(a[i]); 45 } 46 } 47 int main() 48 { 49 int i,j,t,n,x,y,k,op,q; 50 long long m; 51 // freopen("1.in","r",stdin); 52 //freopen("2.out","w",stdout); 53 scanf("%d",&t); 54 for(int cas=1; cas<=t; cas++) 55 { 56 scanf("%d",&n); 57 memset(s1,0,sizeof(s1)); 58 memset(cot,0,sizeof(cot)); 59 que.clear(); 60 for(i=1; i<=n; i++) 61 { 62 scanf("%d",&a[i]); 63 que.push_back(a[i]); 64 } 65 for(i=1; i<=n; i++) 66 { 67 scanf("%d",&b[i]); 68 que.push_back(b[i]); 69 } 70 sort(que.begin(),que.end()); 71 m=unique(que.begin(),que.end())-que.begin(); 72 que.resize(m); 73 int ans=-1; 74 for(i=1; i<=n; i++) 75 { 76 a[i]=getid(a[i]); 77 b[i]=getid(b[i]); 78 } 79 cal(n,a,s1); 80 cal(n,b,s2); 81 long long sum=0; 82 k=0; 83 for(i=1; i<=n; i++) 84 { 85 cot[s1[i]]++; 86 k=max(s1[i],k); 87 sum+=s2[i]; 88 printf("%d ",s1[i]); 89 } 90 m=0; 91 for(i=k+1; i>0; i--) 92 { 93 m=m+cot[i]; 94 sum-=m; 95 if(sum<=0) 96 { 97 break; 98 } 99 } 100 if(sum==0) 101 { 102 i--; 103 for(j=1; j<=n; j++) 104 { 105 if(max(s1[j+i]-i,0)!=s2[j]) 106 { 107 break; 108 } 109 } 110 if(j>n) 111 { 112 sort(a+1,a+n+1); 113 sort(b+1,b+n+1); 114 for(j=1; j<=n; j++) 115 { 116 if(a[j]!=b[j]) 117 { 118 break; 119 } 120 } 121 if(j>n) 122 { 123 ans=i; 124 } 125 } 126 } 127 printf("Case #%d: %d\n",cas,ans); 128 } 129 return 0; 130 131 }