逆序对的定义:长度为n的数组a,求满足i<j时a[i]>a[j]条件的数对个数。
第一次接触这种问题的人可能是更先想到的是n^2去暴力数前面有几个比他大的数。
1 int main() 2 { 3 int n; 4 while(~scanf("%d", &n), n) { 5 ans = 0; 6 for(int i = 1; i <= n; i++) 7 scanf("%d", &a[i]); 8 for(int i = 1; i <= n; i+=2) 9 for(int j = 1; j < i; j+=2) 10 if(a[j] > a[i]) ans++; 11 printf("%d", ans); 12 } 13 return 0; 14 }
n^2算法就是数一下前面有多少个数比现在这个数大 这样全部跑完只后就是逆序数了。
其中重点是 前面有多少个数比现在这个数大
但是每次从1 fo r一遍到i的位置太浪费时间了
所以我们可以用线段树来优化这个数数过程
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lson l,m,rt<<1 4 #define rson m+1,r,rt<<1|1 5 #define LL long long 6 const int N=100005; 7 int sum[N<<2], a[N]; 8 LL ans; 9 void Update(int c, int l, int r,int rt) { 10 if(l == r) { 11 sum[rt]++; 12 return; 13 } 14 int m = l+r >> 1; 15 if(c <= m) Update(c,lson); 16 else Update(c,rson); 17 sum[rt]=sum[rt<<1]+sum[rt<<1|1]; 18 } 19 LL Query(int L, int R, int l, int r, int rt) { 20 if(L <= l && r <= R) 21 return sum[rt]; 22 int m = l+r >> 1; 23 LL cnt = 0; 24 if(L <= m) cnt+=Query(L,R,lson); 25 if(m < R) cnt += Query(L,R,rson); 26 return cnt; 27 } 28 int main() { 29 int n; 30 while(~scanf("%d", &n), n){ 31 ans = 0; 32 memset(sum, 0, sizeof(sum)); 33 for(int i = 1; i <= n; i++){ 34 scanf("%d", &a[i]); 35 } 36 for(int i = 1; i <= n; i++) { 37 ans += Query(a[i],n,1,n,1); 38 Update(a[i],1,n,1); 39 } 40 printf("%d\n", ans); 41 } 42 return 0; 43 }
线段树算法的在求逆序对的关键就是将出现过的数对应的位置标记一下(+1)
假设 i=k时, 查询一下区间 [a[k], n] 的区间和, 这个和就是(j < k && a[j] > a[k]) 的数目
然后在a[k] 的位置 +1
重复这个过程就能求出解了
是不是很疑惑为什么?
当查询区间的时候, 如果在后面的区间内查询到次数不为0时, 说明有几个比他大数在他前面出现过,
重点就是标记。
这里还可以用树状数组来代替线段树。树状数组的特点就是好写,并且速度比线段树快一点。
1 #include<algorithm> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 6 #define LL long long 7 #define ULL unsigned LL 8 #define fi first 9 #define se second 10 #define pb push_back 11 #define lson l,m,rt<<1 12 #define rson m+1,r,rt<<1|1 13 #define max3(a,b,c) max(a,max(b,c)) 14 #define min3(a,b,c) min(a,min(b,c))amespace std; 15 const int N = 500000+5; 16 int tree[N], cnt = 0, A[N]; 17 pair<int, int> P[N]; 18 int lowbit(int x){ 19 return x&(-x); 20 } 21 void Add(int x) { 22 while(x <= cnt) { 23 tree[x]++; 24 x += lowbit(x); 25 } 26 } 27 int Query(int x) { 28 int ret = 0; 29 while(x) { 30 ret += tree[x]; 31 x -= lowbit(x); 32 } 33 return ret; 34 } 35 int main(){ 36 int n; 37 while(~scanf("%d", &n), n) { 38 cnt = 0; 39 for(int i = 1; i <= n; i++) { 40 scanf("%d", &A[i]); 41 } 42 memset(tree, 0, sizeof(tree)); 43 LL ans = 0; 44 for(int i = 1; i <= n; i++) { 45 ans += Query(cnt) - Query(A[i]); 46 Add(A[i]); 47 } 48 printf("%I64d\n", ans); 49 } 50 return 0; 51 }
注意的是 线段树与树状数组求逆序对的时候 数值不能太大 比如在 a[i] <= 1e9的时候 就不能直接用树状数组和逆序数去求了,因为开不了那么大的空间。
但在这个时候,如果n不是很大可以先对数据进行离散化再进行使用树状数组或者线段树处理数据。
题意就是求逆序对, 但是这个a[i]的范围太大,所以我们不能直接进行求解,但是这个地方只于数的相对大小有关,对于数的差值无关,所以我们可以先对所有数离散化,再进行上述的操作。
1 #include<algorithm> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 6 #define LL long long 7 #define ULL unsigned LL 8 #define fi first 9 #define se second 10 #define pb push_back 11 #define lson l,m,rt<<1 12 #define rson m+1,r,rt<<1|1 13 #define max3(a,b,c) max(a,max(b,c)) 14 #define min3(a,b,c) min(a,min(b,c))amespace std; 15 const int N = 500000+5; 16 int tree[N], cnt = 0, A[N]; 17 pair<int, int> P[N]; 18 int lowbit(int x){ 19 return x&(-x); 20 } 21 void Add(int x) { 22 while(x <= cnt) { 23 tree[x]++; 24 x += lowbit(x); 25 } 26 } 27 int Query(int x) { 28 int ret = 0; 29 while(x) { 30 ret += tree[x]; 31 x -= lowbit(x); 32 } 33 return ret; 34 } 35 int main(){ 36 int n; 37 while(~scanf("%d", &n), n) { 38 cnt = 0; 39 for(int i = 1; i <= n; i++) { 40 scanf("%d", &A[i]); 41 P[i].fi = A[i]; 42 P[i].se = i; 43 } 44 sort(P+1, P+1+n); 45 P[0].fi = -1; 46 for(int i = 1; i <= n; i++) { 47 if(P[i-1].fi == P[i].fi) 48 A[P[i].se] = cnt; 49 else cnt++, A[P[i].se] = cnt; 50 } 51 memset(tree, 0, sizeof(tree)); 52 LL ans = 0; 53 for(int i = 1; i <= n; i++) { 54 ans += Query(cnt) - Query(A[i]); 55 Add(A[i]); 56 } 57 printf("%I64d\n", ans); 58 } 59 return 0; 60 }