樹狀數組求逆序對
阿新 • • 發佈:2017-08-07
res gets 逆序 算法 fps string ons 基本 -a
我們知道,求逆序對最典型的方法就是歸並排序,但是還有一種方法就是樹狀數組。假如你理解了樹狀數組,樹狀數組求逆序對相比歸並排序排序要更好理解一些,而且樹狀數組的代碼量也要少一些。
我們先看一下逆序對是什麽吧。
逆序對就是序列a中ai>aj且i<j的有序對。
根據上面的定義我們很快的就可以寫出O(n^2)的算法,即枚舉j,再枚舉所有小於j的i,統計ai>aj的數量。但這個算法當時間復雜度過高。
如果我們能快速的統計出ai>aj的數量,時間復雜度就可以得到很好的提升。
樹狀數組就可以很好的做到這一點。
我們可以先開一個大小為a的最大值的數組t,每當讀入一個數時,我們可以用桶排序的思想,將t[a[i]]加上1,然後我們統計t[1]~t[a[i]]的和ans,ans - 1(除掉這個數本身)就是在這個數前面有多少個數比它小。我們只要用i-ans就可以得出前面有多少數比它大,也就是逆序對的數量。
#include <bits/stdc++.h> #define lowbit(x) (x)&(-x) using namespace std; const int maxn = 1e6+10; int t[maxn],n,result; void add(int x) { while(x<=maxn) { t[x]++; x += lowbit(x); } } int query(int x) { int ans=0; for (;x;x-=lowbit(x)) ans+=t[x];return ans; } int main() { int temp; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&temp); add(temp); result += i - query(temp); } printf("%d\n",result); return 0; }
現在這個代碼可以在數的最大值比較小的時候可以正確的得出答案,如果數據很大,這回造成我們要開的空間很大。
我們是否可以適當的減少空間的需求呢?我們看看下面這些數:
1 2 3 4 5 10
這6個數我們需要使用大小10的數組來存儲,我們仔細想想,可以發現中間 6 7 8 9 這4個位置是沒有用到的,也就是說這4個空間被浪費了。怎樣減少這樣的浪費呢?
我們可以在讀完數數據後對他進行從小到大排序,我們用排完序的數組的下標來進行運算。這樣可以保證小的數依舊小,大的數依舊大。這一步叫做離散化。
struct Node { int v;//數字本身 int order;//原序列的的下標 }a[500005];
int reflect[500005];//用來存儲原數第i個數的order下標是什麽
//計算relect數組
relect[a[i].order] = i;
以上就是離散化的核心部分代碼。
完整程序
代碼來自:http://blog.csdn.net/SeasonJoe/article/details/50193789?locationNum=15&fps=1
#include <iostream> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <algorithm> using namespace std; const int maxn= 500005; int aa[maxn];//離散化後的數組 int c[maxn]; //樹狀數組 int n; struct Node { int v; int order; }a[maxn]; bool cmp(Node a, Node b) { return a.v < b.v; } int lowbit(int k) { return k&(-k); //基本的lowbit函數 } void update(int t, int value) { //即一開始都為0,一個個往上加(+1), int i; for (i = t; i <= n; i += lowbit(i)) c[i] += value; } int getsum(int t) { //即就是求和函數,求前面和多少就是小於它的個數 int i, sum = 0; for (i = t; i >= 1; i -= lowbit(i)) sum += c[i]; return sum; } int main() { int i; while (scanf("%d", &n), n) { for (i = 1; i <= n; i++) //離散化 { scanf("%d", &a[i].v); a[i].order = i; } sort(a + 1, a + n + 1,cmp);//從1到n排序,cmp容易忘 memset(c, 0, sizeof(c)); for (i = 1; i <= n; i++) aa[a[i].order] = i; __int64 ans = 0; for (i = 1; i <= n; i++) { update(aa[i], 1); ans += i - getsum(aa[i]); //減去小於的數即為大於的數即為逆序數 } printf("%I64d\n", ans); } return 0; }
樹狀數組求逆序對