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 }

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 }

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 }

时间: 2024-10-03 21:54:10

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

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 1166 敌兵布阵 (树状数组&#183;线段树)

题意  中文 动态区间和问题   只会更新点  最基础的树状数组 线段树的应用 树状数组代码 #include <bits/stdc++.h> using namespace std; const int N = 50005; int c[N], n, m; void add(int p, int x) { while(p <= n) c[p] += x, p += p & -p; } int getSum(int p) { int ret = 0; while(p > 0

树状数组 线段树

树状数组 树状数组的基本用途是维护序列的前缀和,相比前缀和数组,树状数组优势在于高效率的单点修改,单点增加(前缀和数组单点修改效率比较低) 因为树状数组的思想,原理还是很好理解的,就直接讲基本算法; 1 lowbit函数 关于lowbit这个函数,可能会有点难以理解,但其实你不理解也没关系,把模板背下来就好 根据任意正整数关于2的不重复次幂的唯一分解性质,例如十进制21用二进制表示为10101,其中等于1的位是第0,2,4(最右端是第0位)位,即21被二进制分解成\(2^4+2^2+2^0\);

「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2*s_l-l$的区间$[l+1,r]$其众数为$x$,这个显然可以用一个数据结构来维护. 直接扫一遍效率是$O($数字种类数$*nlogn)$的,无法承受,但是我们发现,对于每一段非$x$的数,$2*s_i-i$是公差为$-1$的等差数列,所以它们对答案的贡献实际上可以一次性计算.设$L$为一段非$x$数

Codeforces Round #225 (Div. 1) C 树状数组 || 线段树

看到这题很开心啊,有印象跟以前做过的很像,貌似最近就做过一个,以时间戳为区间来建立树状数组,然后一开始我以为题意是,给x点加val,它以下的所有节点都加-val:所以一开始就以 加 和 减 建立了两个树状数组,最后 减去就是答案,写完发现跟案例对不上啊,读了题目也没发现读错了,对于那句话 我理解错了,后来看了 这个: http://blog.csdn.net/keshuai19940722/article/details/18967661 仔细看看处理部分,我还以为分奇偶性有规律呢,后来才发现读

hdu1166 敌兵布阵 树状数组/线段树

数列的单点修改.区间求和 树状数组或线段树入门题 1 #include<stdio.h> 2 #include<string.h> 3 4 int c[50005],N; 5 6 void add(int x,int a){ 7 while(x<=N){ 8 c[x]+=a; 9 x+=(x&-x); 10 } 11 return; 12 } 13 14 int sum(int x){ 15 int t=0; 16 while(x){ 17 t+=c[x]; 18 x

hdu 1166 敌兵布阵——(区间和)树状数组/线段树

here:http://acm.hdu.edu.cn/showproblem.php?pid=1166 Input 第一行一个整数T.表示有T组数据. 每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地.接下来有N个正整数,第i个正整数ai代表第i个工兵营地里開始时有ai个人(1<=ai<=50). 接下来每行有一条命令.命令有4种形式: (1) Add i j,i和j为正整数,表示第i个营地添加j个人(j不超过30) (2)Sub i j ,i和j为正整数,表示第i

HDU1166 敌兵布阵 树状数组|线段树-构造完全二叉树(理解)

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 57787 Accepted Submission(s): 24420 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这