首页 > 代码库 > hdu1394(枚举/树状数组/线段树单点更新&区间求和)

hdu1394(枚举/树状数组/线段树单点更新&区间求和)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394

 

题意:给出一个循环数组,求其逆序对最少为多少;

 

思路:对于逆序对: 交换两个相邻数,逆序数 +1 或 -1, 交换两个不相邻数 a, b, 逆序数 += 两者间大于 a 的个数 - 两者间小于 a 的个数;

所以只要求出初始时的逆序对数,就可以推出其余情况时的逆序对数.对于求初始逆序对数,这里 n 只有 5e3,可以直接暴力 / 树状数组 / 线段树 / 归并排序;

 

代码:

1.直接暴力

技术分享
 1 #include <iostream>
 2 #include <stdio.h>
 3 using namespace std;
 4 
 5 const int MAXN = 5e3 + 10;
 6 int a[MAXN];
 7 
 8 int  main(void){
 9     int n, ans = 0;
10     while(~scanf("%d", &n)){
11         ans = 0;
12         for(int i = 0; i < n; i++){
13             scanf("%d", &a[i]);
14             for(int j = 0; j < i; j++){
15                 if(a[j] > a[i]) ans++;
16             }
17         }
18         int cnt = ans;
19         for(int i = 0; i < n; i++){
20             cnt += (n - a[i] - 1) - a[i];
21             ans = min(ans, cnt);
22         }
23         printf("%d\n", ans);
24     }
25     return 0;
26 }
View Code

 

2.树状数组

技术分享
 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 using namespace std;
 5 
 6 const int MAXN = 5e3 + 10;
 7 int tree[MAXN], a[MAXN], n;
 8 
 9 int lowbit(int x){
10     return x & (-x);
11 }
12 
13 void updata(int x, int d){
14     while(x <= n){
15         tree[x] += d;
16         x += lowbit(x);
17     }
18 }
19 
20 int sum(int x){
21     int ans = 0;
22     while(x > 0){
23         ans += tree[x];
24         x -= lowbit(x);
25     }
26     return ans;
27 }
28 
29 int main(void){
30     int ans = 0;
31     while(~scanf("%d", &n)){
32         ans = 0;
33         memset(tree, 0, sizeof(tree));
34         for(int i = 1; i <= n; i++){
35             scanf("%d", &a[i]);
36             updata(a[i] + 1, 1);
37             ans += i - sum(a[i] + 1);//当前是第 i 个数,减去a[i]前面(这里包括了a[i])的数就是a[i]后面的数了,即可以和a[i]组成逆序对的数的数目
38             // ans += sum(n) - sum(a[i] + 1);
39         }
40         int cnt = ans;
41         for(int i = 1; i <= n; i++){
42             cnt += (n - a[i] - 1) - a[i];
43             ans = min(ans, cnt);
44         }
45         printf("%d\n", ans);
46     }
47     return 0;
48 }
View Code

 

3.线段树

技术分享
 1 #include <iostream>
 2 #include <stdio.h>
 3 #define lson l, mid, rt << 1
 4 #define rson mid + 1, r, rt << 1 | 1
 5 using namespace std;
 6 
 7 const int MAXN = 5e3 + 10;
 8 int sum[MAXN << 2], a[MAXN];
 9 
10 void push_up(int rt){
11     sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
12 }
13 
14 void build(int l, int r, int rt){
15     sum[rt] = 0;
16     if(l == r) return;
17     int mid = (l + r) >> 1;
18     build(lson);
19     build(rson);
20 }
21 
22 void update(int p, int x, int l, int r, int rt){
23     if(l == r){
24         sum[rt] += x;
25         return;
26     }
27     int mid = (l + r) >> 1;
28     if(p <= mid) update(p, x, lson);
29     else update(p, x, rson);
30     push_up(rt);
31 }
32 
33 int query(int L, int R, int l, int r, int rt){
34     if(l >= L && r <= R) return sum[rt];
35     int mid = (l + r) >> 1;
36     int ans = 0;
37     if(L <= mid) ans += query(L, R, lson);
38     if(R > mid) ans += query(L, R, rson);
39     return ans;
40 }
41 
42 int main(void){
43     int n, ans = 0;
44     while(~scanf("%d", &n)){
45         ans = 0;
46         build(0, n-1, 1);
47         for(int i = 0; i < n; i++){
48             scanf("%d", &a[i]);
49             ans += query(a[i], n - 1, 0, n - 1, 1);
50             update(a[i], 1, 0, n - 1, 1);
51         }
52         int cnt = ans;
53         for(int i = 0; i < n; i++){
54             cnt += (n - a[i] - 1) - a[i];
55             ans = min(ans, cnt);
56         }
57         printf("%d\n", ans);
58     }
59     return 0;
60 }
View Code

 

hdu1394(枚举/树状数组/线段树单点更新&区间求和)