Contents
  1. 1. 网上转的一篇解题报告有蛮详细的

网上转的一篇解题报告有蛮详细的

链接:没找到是哪个博客的了,当时忘保存了,出处是csdn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253

1/*
2 树状数组,具体的说是 离散化+树状数组。这也是学习树状数组的第一题.
3 算法的大体流程就是:
4 1.先对输入的数组离散化,使得各个元素比较接近,而不是离散的,
5 2.接着,运用树状数组的标准操作来累计数组的逆序数。
6 算法详细解释:
7 1.解释为什么要有离散的这么一个过程?//为了后面用树状数组
8 刚开始以为999.999.999这么一个数字,对于int存储类型来说是足够了。
9 还有只有500000个数字,何必要离散化呢?
10 刚开始一直想不通,后来明白了,后面在运用树状数组操作的时候,
11 用到的树状数组C[i]是建立在一个有点像位存储的数组的基础之上的,
12 不是单纯的建立在输入数组之上。
13 比如输入一个9 1 0 5 4,那么C[i]树状数组的建立是在,
14
15 数据:9 1 0 5 4 p[i].val
16 编号:1 2 3 4 5 p[i].oder = i*************
17 sort
18 数据:0 1 4 5 9
19 编号:3 2 5 4 1
20 顺序:1 2 3 4 5
21
22 a[p[i].编号] = 顺序号;**********************
23
24 a[3] = 1<--0;
25 a[2] = 2<--1;
26 a[5] = 3<--4;
27 a[4] = 4<--5;
28 a[1] = 5<--9;
29
30 a[]={ 5 2 1 4 3 }
31
32 新号:1 2 3 4 5
33 值 :
34
35
36 下标 0 1 2 3 4 5 6 7 8 9
37 数组 1 1 0 0 1 1 0 0 0 1
38 现在由于999999999这个数字相对于500000这个数字来说是很大的,
39 所以如果用数组位存储的话,那么需要999999999的空间来存储输入的数据。
40 这样是很浪费空间的,题目也是不允许的,所以这里想通过离散化操作,
41 使得离散化的结果可以更加的密集。
42 2. 怎么对这个输入的数组进行离散操作?
43 离散化是一种常用的技巧,有时数据范围太大,可以用来放缩到我们能处理的范围;
44 因为其中需排序的数的范围0---999 999 999;显然数组不肯能这么大;
45 而N的最大范围是500 000;故给出的数一定可以与1.。。。N建立一个一一映射;
46 ①当然用map可以建立,效率可能低点;
47 ②这里用一个结构体
48 struct Node
49 {
50 int v,ord;
51 }p[510000];和一个数组a[510000];
52
53 其中v就是原输入的值,ord是下标;
54 然后对结构体按v从小到大排序;
55
56 此时,v和结构体的下标就是一个一一对应关系,
57 而且满足原来的大小关系;
58
59 for(i=1;i<=N;i++)
60 a[p[i].ord]=i;
61
62 然后a数组就存储了原来所有的大小信息;
63 比如 9 1 0 5 4 ------- 离散后aa数组
64 就是 5 2 1 4 3;
65 具体的过程可以自己用笔写写就好了。
66 3. 离散之后,怎么使用离散后的结果数组来进行树状数组操作,计算出逆序数?
67 如果数据不是很大, 可以一个个插入到树状数组中,
68 每插入一个数, 统计比他小的数的个数,
69 对应的逆序为 i- getsum( aa[i] ),
70 其中 i 为当前已经插入的数的个数,
71 getsum( aa[i] )为比 aa[i] 小的数的个数,
72 i- sum( aa[i] ) 即比 aa[i] 大的个数, 即逆序的个数
73 但如果数据比较大,就必须采用离散化方法
74 假设输入的数组是9 1 0 5 4, 离散后的结果aa[] = {5,2,1,4,3};
75 在离散结果中间结果的基础上,那么其计算逆序数的过程是这么一个过程。
76 1,输入5, 调用upDate(5, 1),把第5位设置为1
77 1 2 3 4 5
78 0 0 0 0 1
//后面都是计算1-n上小于等于n的数的总数,而不是小于n的数的总数,是考虑了重复的情况
79 计算1-5上比5小(小于等于5)的数字存在么? 这里用到了树状数组的getSum(5) = 1操作,
80 现在用输入的下标1 - getSum(5) = 0 就可以得到对于5的逆序数为0。
81 2. 输入2, 调用upDate(2, 1),把第2位设置为1
82 1 2 3 4 5
83 0 1 0 0 1
84 计算1-2上比2小(小于等于2)的数字存在么? 这里用到了树状数组的getSum(2) = 1操作,
85 现在用输入的下标2 - getSum(2) = 1 就可以得到对于2的逆序数为1。
86 3. 输入1, 调用upDate(1, 1),把第1位设置为1
87 1 2 3 4 5
88 1 1 0 0 1
89 计算1-1上比1小(小于等于1)的数字存在么? 这里用到了树状数组的getSum(1) = 1操作,
90 现在用输入的下标 3 - getSum(1) = 2 就可以得到对于1的逆序数为2。
91 4. 输入4, 调用upDate(4, 1),把第4位设置为1
92 1 2 3 4 5
93 1 1 0 1 1
94 计算1-4上比4小(小于等于1)的数字存在么? 这里用到了树状数组的getSum(4) = 3操作,
95 现在用输入的下标4 - getSum(4) = 1 就可以得到对于4的逆序数为1。
96 5. 输入3, 调用upDate(3, 1),把第3位设置为1
97 1 2 3 4 5
98 1 1 1 1 1
99 计算1-3上比3小(小于等于3)的数字存在么? 这里用到了树状数组的getSum(3) = 3操作,
100 现在用输入的下标5 - getSum(3) = 2 就可以得到对于3的逆序数为2。
101 6. 0+1+2+1+2 = 6 这就是最后的逆序数
102 分析一下时间复杂度,首先用到快速排序,时间复杂度为O(NlogN),
103 后面是循环插入每一个数字,每次插入一个数字,分别调用一次upData()和getSum()
104 外循环N, upData()和getSum()时间O(logN) => 时间复杂度还是O(NlogN).
105 */





代码:
版本一:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;

const int N = 500005;

struct Node{
int val;//储存原始的数据
int pos;//储存原始的下标
};

Node node[N];
int n;
int tree[N], reflect[N];//树状数组,映射数组

bool cmp(const Node &a, const Node &b)
{

return a.val < b.val;
}

int lowbit(int x)
{

return x & -x;
}

void update(int index, int val)
{

while(index <= n){
tree[index] += val;
index += lowbit(index);
}
}

int getsum(int index)
{

int sum = 0;
while(index > 0){
sum += tree[index];
index -= lowbit(index);
}
return sum;
}

int main()
{

while(scanf("%d", &n) != EOF && n){
for(int i = 1; i <= n; i++){
scanf("%d", &node[i].val);
node[i].pos = i;
}
sort(node + 1, node + 1 + n, cmp);//将结构数组里的元素根据val从小到大排序
for(int i = 1; i <= n; i++){
reflect[node[i].pos] = i;//离散化
}
for(int i = 1; i <= n; i++)
tree[i] = 0;//初始化树状数组
long long ans = 0;
for(int i = 1; i <= n; i++){
update(reflect[i], 1);
ans += i - getsum(reflect[i]); //i减去小于等于他的个数得出比他大个数
}
printf("%lld\n", ans);
}
return 0;
}


///////////////////////////////////////////////////////////
版本二:
离散化借助了两个数组,需要用二分查找
1. #include<cstdio>
2. #include<cstring>
3. #include<iostream>
4. #include<algorithm>
5. using namespace std;
6.
7. const int MAXN = 500010;
8.
9. int n;
10. int tmpNum[MAXN] , num[MAXN];
11. int treeNum[MAXN];
12.
13. int lowbit(int x){
14. return x&(-x);
15. }
16.
17. int getSum(int x){
18. int sum = 0;
19. while(x){
20. sum += treeNum[x];
21. x -= lowbit(x);
22. }
23. return sum;
24. }
25.
26. void add(int x , int val){
27. while(x < MAXN){
28. treeNum[x] += val;
29. x += lowbit(x);
30. }
31. }
32.
33. int search(int x , int len){
34. int left = 1;
35. int right = len;
36. while(left <= right){
37. int mid = (left+right)>>1;
38. if(num[mid] == x)
39. return mid;
40. else if(num[mid] < x)
41. left = mid+1;
42. else
43. right = mid-1;
44. }
45. }
46.
47. long long solve(){
48. long long ans = 0;
49. memcpy(tmpNum , num , sizeof(num));
50. memset(treeNum , 0 , sizeof(treeNum));
51. sort(num+1 , num+1+n);
52. int len = unique(num+1 , num+1+n)-(num+1);
53. for(int i = 1 ; i <= n ; i++){
54. int id = search(tmpNum[i] , len);
55. ans += i-getSum(id)-1;
56. add(id , 1);
57. }
58. return ans;
59. }
60.
61. int main(){
62. while(scanf("%d" , &n) && n){
63. for(int i = 1 ; i <= n ; i++)
64. scanf("%d" , &num[i]);
65. printf("%lld\n" , solve());
66. }
67. return 0;
68. }
Contents
  1. 1. 网上转的一篇解题报告有蛮详细的