hihocoder1524

题目链接

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

给定一个1-N的排列A1, A2, ... AN,如果Ai和Aj满足i < j且Ai > Aj,我们就称(Ai, Aj)是一个逆序对。  

求A1, A2 ... AN中所有逆序对的数目。  

输入

第一行包含一个整数N。  

第二行包含N个两两不同整数A1, A2, ... AN。(1 <= Ai <= N)  

对于60%的数据 1 <= N <= 1000  

对于100%的数据 1 <= N <= 100000

输出

一个整数代表答案

样例输入
5
3 2 4 5 1
样例输出
5
 求逆序对,两种方法,一个是用树形数组做,一个是用归并排序做。两种都试了一遍,有一个地方比较坑就是数组类型必须是long long。不然会WA,题目却没有明显信息说要long long。。这个地方可以说是很坏坏了。
 
树形数组ac代码:
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<vector>
 6 #include<iomanip>
 7 using namespace std;
 8 
 9 
10 //1524 : 逆序对
11 const int maxn = 1e5 + 5;
12 int n;
13 long long a[maxn], b[maxn];
14 int lowbit(int i)
15 {
16     return i&-i;
17 }
18 void update(int i)
19 {
20     while (i <= n)
21     {
22         b[i]++;
23         i += lowbit(i);
24     }
25 }
26 long long sum(int i)
27 {
28     long long cnt = 0;
29     while (i)
30     {
31         cnt += b[i];
32         i -= lowbit(i);
33     }
34     return cnt;
35 }
36 int main()
37 {
38     cin >> n;
39     for (int i = 1;i <= n;i++)
40     {
41         cin >> a[i];
42     }
43     memset(b, 0, sizeof(b));
44     long long res = 0;
45     for (int i = n;i >= 1;i--)
46     {
47         update(a[i]);
48         res += sum(a[i] - 1);
49     }
50     cout << res << endl;
51     return 0;
52 }

树形数组这个东西还是比较有趣的。这个算法可以说是比较天才了。

具体的树形数组详解可以看这个博主的http://blog.csdn.net/ljd4305/article/details/10101535

 

 

归并排序的ac代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #include<iostream>
 5 #include<vector>
 6 #include<iomanip>
 7 using namespace std;
 8 
 9 
10 //1524 : 逆序对
11 const int maxn = 1e5 + 5;
12 int n;
13 long long a[maxn],b[maxn];
14 long long cnt;
15 
16 void merge_sort(long long * a, int l, int r)
17 {
18     int mid = (l + r) >> 1;
19     int m = l, n = mid + 1;
20     int pos = l;
21     while (m<=mid&&n<=r)
22     {
23         if (a[m] < a[n])
24         {
25             b[pos++] = a[m++];
26         }
27         else
28         {
29             b[pos++] = a[n++];
30             cnt+=mid-m+1;
31         }
32     }
33 
34     while (m <= mid) b[pos++] = a[m++];
35     while (n <= r) b[pos++] = a[n++];
36     for (int i = l;i <= r;i++)
37         a[i] = b[i];
38 }
39 void merge(long long * a, int l, int r)
40 {
41     if (l >= r) return;
42     int mid = (l + r) >>1;
43     merge(a, l, mid);
44     merge(a, mid+1, r);
45     merge_sort(a, l, r);
46 }
47 
48 int main()
49 {
50     cnt = 0;
51     cin >> n;
52     for (int i = 1;i <= n;i++)
53     {
54         cin >> a[i];
55     }
56     merge(a, 1, n);
57     cout << cnt << endl;
58     return 0;
59 }

归并排序是将数列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。因此,可以在归并排序中的合并过程中计算逆序数。

可以参考http://blog.csdn.net/acdreamers/article/details/16849761

 

posted on 2017-06-24 20:40  炮二平五  阅读(260)  评论(0编辑  收藏  举报

导航