树状数组求两种逆序对 || P1908 逆序对
1.对于给定的一段正整数序列,逆序对就是序列中 i < j && a[i] > a[j] 的有序对。
首先可以运用归并排序来求(可惜我还没有完全自然地理解归并排序),好消息是树状数组也可以求。
那么对于逆序对有两种,一种较为简单的是给定的数组是一个排列(意味着没有重复数组并且最大数字n可能较小),在这个情况下,我们可以将这个静态的问题转化为动态的问题(类似于扫描线的想法):
我们开一个树状数组不断维护我们这个序列此时的情况,数组里维护的是在j ~ n中有多少a[i] > a[j],那么树状数组c中按照序列从左到右将数据的值对应的位置的数加一,代表又有一个数出现,那么这个数字后面的数的和就是这个数所构成的逆序对的数量。那么其实就是单点修改和区间查询,已经差不多是树状数字板子了,但是还有一个小问题就是我们要求的是这个数到n之间的和,而树状数组只能求前缀和,我们只需要读入a[i]是作a[i] = n + 1 - a[i]即可(将1变成n),此时就相当于求后缀了。
这种静态转化成动态的思想值得思考。
代码 :
1 #include <bits/stdc++.h> 2 #define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL); 3 using namespace std; 4 //using i64 = long long; 5 const string YES = "Yes"; 6 const string NO = "No"; 7 template <typename T> 8 class BIT { 9 public: 10 vector<T> c; 11 int n; 12 BIT(int _n) : n(_n) { 13 c.resize(n); 14 } 15 void modify(int x, T v) { 16 while (x < n) { 17 c[x] += v; 18 x |= (x + 1); 19 } 20 } 21 T query(int x) { 22 T v{}; 23 while (x >= 0) { 24 v += c[x]; 25 x = (x & (x + 1)) - 1; 26 } 27 return v; 28 } 29 T get(int l, int r) { 30 return query(r) - query(l - 1); 31 } 32 }; 33 int32_t main() { 34 gogo; 35 int n; 36 cin >> n; 37 BIT<int> c(n + 1); 38 int ans = 0; 39 for (int i = 1;i <= n;i ++) { 40 int x; 41 cin >> x; 42 x = n + 1 - x; 43 ans += c.query(x); 44 c.modify(x, 1); 45 } 46 cout << ans << endl; 47 }
2.对于给定的一段正整数序列,逆序对就是序列中 i < j && a[i] > a[j] 的有序对,但是此时不保证数组是排列(意味着有重复数字并且可能数字较大)。
此时遇到的问题就是如果数字范围是1e9,那么显然数组是开不了这么大的,但是这时候其实我们需要的只是一个相对大小的关系而不需要绝对大小关系,也就是意味着我们可以通过一次离散化来对原数组变成一个范围为n的新数组,不过这时候要注意如果有重复数字并且不处理的话,那么就会WA因为答案会多算,所以我们离散化的步骤是:首先记录一下每个数字的大小以及在原数组的位置,然后进行排序,排序按照大小作为第一关键字,以出现的顺序作为第二关键字(思考),然后离散化赋值,最后求逆序对的步骤就和排列的版本一模一样了。
代码 :
1 #include <bits/stdc++.h> 2 #define gogo ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL); 3 using namespace std; 4 //using i64 = long long; 5 const string YES = "Yes"; 6 const string NO = "No"; 7 template <typename T> 8 class BIT { 9 public: 10 vector<T> c; 11 int n; 12 BIT(int _n) : n(_n) { 13 c.resize(n); 14 } 15 void modify(int x, T v) { 16 while (x < n) { 17 c[x] += v; 18 x |= (x + 1); 19 } 20 } 21 T query(int x) { 22 T v{}; 23 while (x >= 0) { 24 v += c[x]; 25 x = (x & (x + 1)) - 1; 26 } 27 return v; 28 } 29 T get(int l, int r) { 30 return query(r) - query(l - 1); 31 } 32 }; 33 struct Node { 34 int v, idx; 35 }; 36 bool cmp(Node x, Node y) { 37 if (x.v == y.v) 38 return x.idx < y.idx; 39 return x.v < y.v; 40 } 41 int32_t main() { 42 gogo; 43 int n; 44 cin >> n; 45 vector<Node> a(n + 1); 46 for (int i = 1;i <= n;i ++) { 47 cin >> a[i].v; 48 a[i].idx = i; 49 } 50 sort(a.begin() + 1, a.end(), cmp); 51 vector<int> hs(n + 1); 52 for (int i = 1;i <= n;i ++) 53 hs[a[i].idx] = n + 1 - i; 54 BIT<int> c(n + 1); 55 long long ans = 0; 56 for (int i = 1;i <= n;i ++) { 57 ans += c.query(hs[i]); 58 c.modify(hs[i], 1); 59 } 60 cout << ans << endl; 61 }
巧妙。
https://www.luogu.com.cn/problem/P1908
拓展: https://www.luogu.com.cn/problem/P1774 , https://www.luogu.com.cn/problem/P4378

浙公网安备 33010602011771号