柚子快报激活码778899分享:逆序数

http://yzkb.51969.com/

1.定义

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

举个例子:

标准列是1 2 3 4 5

那么 5 4 3 2 1 的逆序数算法:

看第二个,4之前有一个5,在标准列中5在4的后面,所以记1个 类似的,

第三个 3 之前有 4 5 都是在标准列中3的后面,所以记2个 同样的,

2 之前有3个,

1之前有4个

将这些数加起来就是逆序数=1+2+3+4=10

 

再举一个 2 4 3 1 5

4 之前有0个

3 之前有1个

1 之前有3个

5 之前有0个

所以逆序数就是1+3=4

 

 

2.求法

1.朴素方法,两层循环,时间复杂度(O (n^2))

1 int count=0;

2 for(i=0; i

3 {

4 for(j=i+1; j

5 {

6 if(a[i]>a[j])

7 {

8 count++;

9 }

10 }

11 }

 

 2.归并排序,时间复杂度(O(n log n))

 

归并排序是将数列a[l,h]分成两半a[l,mid]和a[mid+1,h]分别进行归并排序,然后再将这两半合并起来。在合并的过程中(设l<=i<=mid,mid+1<=j<=h),当a[i]<=a[j]时,并不产生逆序数;当a[i]>a[j]时,在前半部分中比a[i]大的数都比a[j]大,将a[j]放在a[i]前面的话,逆序数要加上mid+1-i。因此,可以在归并排序中的合并过程中计算逆序数.

现在以6 1 7 2为例,我们以7 2这一块来说,归并排序中当7进入临时空间的时候,看看6 1这一块还剩下几个元素没有入临时空间,剩下的元素必定比7大,剩下的元素个数就是由于7产生的逆序对数目,同样地1产生的逆序对数目类似统计,同样,由于不同块之间互不影响,递归解决此问题。说的有点乱,但仔细想想是这个道理!!!!!

1 #include

2 #include

3 #include

4 #define maxn 500010

5 #define ll long long int

6 using namespace std;

7 ll a[maxn];

8 ll temp[maxn];

9 ll sum;

10 void Merge(int l,int r,int m)

11 {

12 int i=l;

13 int j = m + 1;

14 int k = l;

15 while(i<=m&&j<=r)

16 {

17 if(a[i]>a[j])

18 {

19 sum+=m-i+1;///剩下的没有进入临时空间的元素的个数

20 temp[k++]=a[j++];

21 }

22 else

23 {

24 temp[k++]=a[i++];

25 }

26 }

27 while(i<=m)///将剩余的元素存到数组中

28 {

29 temp[k++]=a[i++];

30 }

31 while(j<=r)

32 {

33 temp[k++]=a[j++];

34 }

35 for(i=l; i<=r; i++)

36 {

37 a[i]=temp[i];

38 }

39 }

40 void mergesort(int l,int r)

41 {

42 if(l

43 {

44 int m = (l + r) / 2;

45 mergesort(l,m);///左二分排序

46 mergesort(m+1,r);///右二分排序

47 Merge(l,r,m);///合并两个升序数组

48 }

49 }

50 int main()

51 {

52 int n,i;

53 while(scanf("%d",&n)!=EOF)

54 {

55 if(n==0)

56 {

57 break;

58 }

59 for(i=0; i

60 {

61 scanf("%lld",&a[i]);

62 }

63 sum=0;

64 mergesort(0,n-1);

65 printf("%lld ",sum);

66 }

67 return 0;

68 }

 

 

3.树状数组

由于树状数组的特性,求和是从当前节点往前求,所以,这里要查询插入当前数值之时,要统计有多少个小于该数值的数还没插入,这些没插入的数,都会在后面插入,也就形成了逆序数。

假设我们将 序列 6 1 2 7 3 4 8 5 存入数组a【】 中, a【1】=6 , a【2】=1...

那么每次,我们可以将 a【i】 插入到 树状数组中,并赋值为 1, 我们求和sum,sum 是1 到 a【i】的和 , 那么这个 sum 表示的值就是当前比a【i】小的数量(包括它本身);而当前一共有 i 个数 , 所以 当前 比a【i】大的数量就是 i - sum;所以 我们统计所有的 i - sum , 它们的和就是逆序数。

 

1 #include

2 #include

3 #include

4 #include

5 using namespace std;

6 #define LL long long

7 #define N 1005*1005

8 LL ans;

9 int a[N];

10 int n,c[N];

11 int lowbit(int x)

12 {

13 return x&-x;

14 }

15 int Getsum(int x)

16 {

17 int ret = 0;

18 while(x>0)

19 {

20 ret+=c[x];

21 x-=lowbit(x);

22 }

23 return ret;

24 }

25

26 void add(int x,int d)

27 {

28 while(x<=n)

29 {

30 c[x]+=d;

31 x+=lowbit(x);

32 }

33 }

34 int main()

35 {

36 int i,j,k,l,r,t;

37 scanf("%d",&n);

38 memset(c,0,sizeof(c));

39 for(i = 1; i<=n; i++)

40 {

41 scanf("%d",&a[i]);

42 }

43 ans = 0;

44 for(i = 1; i<=n; i++)

45 {

46 add(a[i],1);///这里将从c[i]赋值为1更像是一种存在,1代表着存在,0不存在

47 ans+=i-Getsum(a[i]);

48 }

49 printf("%lld\n",ans);

50 return 0;

51 }

4.线段树

用线段树来求逆序数的思路关键在于,线段树是维护一个区间的,所以,对于这种连续区间求逆序数,完全可以判断当插入一个新的数字时,若比它大的数字已经插入了,说明排在了它的前面,也就是产生了这些逆序数。其实线段树与树状数组只是两种不同的数据结构,但在逆序对的处理上二者其实是相同的,树状数组有Getsum()函数求1 到 a【i】的和,而线段树则可以使用Query()来查询。

1 #include

2 #include

3 #include

4 #define MAX 51000

5 #define MID(a,b) (a+b)>>1

6 #define R(a) (a<<1|1)

7 #define L(a) a<<1

8 typedef struct

9 {

10 int num,left,right;

11 } Node;

12 int ans[MAX];

13 Node Tree[MAX<<2];

14 int n;

15 void Build(int t,int l,int r) //以1为根节点建立线段树

16 {

17 int mid;

18 Tree[t].left=l,Tree[t].right=r;

19 if(Tree[t].left==Tree[t].right)

20 {

21 Tree[t].num=0;

22 return ;

23 }

24 mid=MID(Tree[t].left,Tree[t].right);

25 Build(L(t),l,mid);

26 Build(R(t),mid+1,r);

27 }

28

29 void Insert(int t,int l,int r,int x) //向以1为根节点的区间[l,r]插入数字1

30 {

31 int mid;

32 if(Tree[t].left==l&&Tree[t].right==r)

33 {

34 Tree[t].num+=x;

35 return ;

36 }

37 mid=MID(Tree[t].left,Tree[t].right);

38 if(l>mid)

39 {

40 Insert(R(t),l,r,x);

41 }

42 else if(r<=mid)

43 {

44 Insert(L(t),l,r,x);

45 }

46 else

47 {

48 Insert(L(t),l,mid,x);

49 Insert(R(t),mid+1,r,x);

50 }

51 Tree[t].num=Tree[L(t)].num+Tree[R(t)].num;

52 }

53 int Query(int t,int l,int r) //查询以1为根节点,区间[l,r]的和

54 {

55 int mid;

56 if(Tree[t].left==l&&Tree[t].right==r)

57 return Tree[t].num;

58 mid=MID(Tree[t].left,Tree[t].right);

59 if(l>mid)

60 {

61 return Query(R(t),l,r);

62 }

63 else if(r<=mid)

64 {

65 return Query(L(t),l,r);

66 }

67 else

68 {

69 return Query(L(t),l,mid)+Query(R(t),mid+1,r);

70 }

71 }

72 int main()

73 {

74 int a,n,i,t;

75 scanf("%d",&t);

76 long long int k;

77 while(t--)

78 {

79 scanf("%d",&n);

80 memset(Tree,0,sizeof(Tree)); //初始化线段树

81 Build(1,1,n);

82 for(i=1; i<=n; i++) //输入n个数

83 {

84 scanf("%d",&ans[i]);

85 }

86 for(i=1,k=0; i<=n; i++)

87 {

88 a=ans[i];

89 Insert(1,a,a,1); //把线段树[ans[i],ans[i]]区间的值插入为1

90 k=k+(i-Query(1,1,a)); //查询区间[1,ans[i]]值的总和,逆序数等于i-[1,ans[i]]

91 }

92 printf("%lld\n",k);

93 }

94 return 0;

95 }

 

柚子快报激活码778899分享:逆序数

http://yzkb.51969.com/

推荐阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。