樹狀陣列 (離散化 樹狀陣列 求逆序對)

2022-02-18 22:49:19 字數 3403 閱讀 9638

sample test(s)

input

52 3 1 5 4

output

3【題目大意】:求逆序對的個數

【題目分析】

求逆序對有很多方法,比如說用合併排序、分治、樹狀陣列、線段樹,甚至連暴力(氣泡排序)也可以做,但是要注意會不會超時。

這裡就講一下樹狀陣列的方法,這一題最有意思的是離散化的方法,這個方法在處理大資料的排序方面很有用,離散化能夠有效的降低時空複雜度,他可以改進乙個低效的演算法。除了加上了乙個離散化,其他的用樹狀陣列就可以解決。

1、什麼是離散化?

用我的理解來說就是一種對映,為什麼能用離散化呢?或者說離散化能用在哪些方面呢?

舉個例子說吧 ,在排序、求逆序對這些和順序有關的題目中就能用離散化。搜尋帖子你會發現有各種說法,比如「排序後處理」、「對座標的近似處理」等等。哪個是對的呢?哪個都對。關鍵在於,這需要一些例子和不少的講解才能完全解釋清楚。

下面就模擬乙個數列的離散化:

首先定義乙個結構體:

struct node

;node node[max];

和乙個陣列a[max],然後輸入五個數:8 1 6 7 4

for(i=1;i<=n;++i)

node[i].num儲存了陣列的值,而node[i].index儲存的是他的下標,也就是序號。

按照num來進行排序:

bool cmp(node a,node b)

這樣就將原來的8 1 6 7 4轉化為5 1 3 4 2,比較一下這個序列和原來的序列有什麼區別?你會發現他記錄了原來陣列的大小和元素順序,這兩個就通過離散化很好的結合在一起了,而且將原來很大的資料壓縮為一串從1開始的連續的數,大大降低了時空複雜度。

然後問題就簡單了,將a陣列更新到樹狀陣列當中去,進行統計就出答案了。

開始的時候我一直都沒弄清楚樹狀陣列是怎麼實現求逆序對的,後來也是看別人的部落格,模擬了一下才搞懂的。

原理是什麼呢?

在插入a[i]之前,我們先統計比a[i]小的數或等於a[i]的數有幾個,也就是getsum(a[i]),然後再用i-getsum(a[i]),這樣就得到了在他前面並且比他大的資料的個數。這樣也很好理解,總數-小於或等於本身的個數=大於本身的個數,先更新再統計。

還有這題需要用long long ,開始的時候沒想到害我wrong了好多次,然後靜下心來算了一算發現確實要用long long,在從下往上累加的時候,最上面的那個數最大時相當於65537的平方,65537的平方就是四十多億,int最多二十億,妥妥的超了。用long long就過了。還有乙個細節,就是當輸入的序列中有數字相同時要怎麼處理?首先處理這個問題時你得透徹的知道離散化的過程。

有兩種解決方法:

方法一:在離散化的過程中進行處理,也就是在離散化過程中遇到兩個數相同時,你得將它標記為兩個數,這樣就避免了相同時只計算乙個數這種情況。

具體怎麼來實現呢?很簡單,看**,不解釋:

for(i=1;i<=n;i++)

//qsort(a+1,n,sizeof(a[1]),cmp);

sort(a+1,a+n+1,cmp);

b[1]=1;

x1=1;

for(i=2;i<=n;i++)

for(i=1;i<=n;i++)

方法二:直接用stable_sort,即:

stable_sort(node+1,node+n+1,cmp);

現在就來講一下sort和stable_sort的區別:

這兩個函式的原理都是快速排序,時間複雜度在所有排序中最低,為o(nlog2n) ;但是stable_sort要稍微慢一點。

sort的應用;

1、可以傳入兩個引數;

sort(a,a+n) ,其中a是陣列,a+n表示對a[0]至a[n-1]的n個數進行排序(預設從小到大排序);

2、傳入三個引數;

sort(a,a+n,cmp),第三個引數是乙個函式 ;

如果讓函式從大到小排序,可以用如下演算法實現;

bool cmp(int a,int b);

sort(a,a+n,cmp);

而stable_sort的用法與sort一致,區別是stable_sort函式遇到兩個數相等時,不對其交換順序;這個應用在陣列裡面不受影響,當函式引數傳入的是結構體時,會發現兩者之間的明顯區別。

這題如果在離散化的時候不進行處理,後面又用sort,肯定妥妥的跪了,我就是這樣啊,血的教訓。。。

最好的解決辦法就是不管什麼情況下都用stable_sort,這樣就不會出現這種問題了。

下面貼一下**:

方法一:

#include#include#include#includeusing namespace std;

struct point

a[65540];

int b[65540],c[65540];

int cmp(point a,point b)

return sum;

}int main()

//qsort(a+1,n,sizeof(a[1]),cmp);

sort(a+1,a+n+1,cmp);

b[1]=1;

x1=1;

for(i=2;i<=n;i++)

for(i=1;i<=n;i++)

printf("%i64d\n",num);

} return 0;

}

方法二:

#include#include#include#include#include#include#define max 70000

using namespace std;

long long tree[max];

long long a[max];

struct node

;node node[max];

bool cmp(node a,node b)

return sum;

}int main()

stable_sort(node+1,node+n+1,cmp);

// check

// for(i=1;i<=n;i++)

//

//對排序後的陣列進行離散化

for(i=1;i<=n;i++)

// for(i=1;i<=n;i++)

//

//入樹+統計

long long ans=0;

for(i=1;i<=n;i++)

{update(a[i]);

// cout<

樹狀陣列求逆序對及離散化

樹狀陣列求逆序對及離散化 逆序對指的是乙個序列中有兩個數ai和aj,iaj,即它們下標與數值的增減不一致,那麼對於這個問題 求乙個序列中逆序對的個數,該如何解決呢?我最初接觸到的方法是歸併排序,是個很不錯的方法,但是對於向我一樣的蒟蒻 還是有理解難度,而今天講的樹狀陣列解法,至少 理解難度降低了不少...

逆序對 離散樹狀陣列

求逆序對有三種以上方法 1 離散樹狀陣列,2 線段樹,3 歸併排序 今天做了下洛谷的p1908逆序對 1 一開始用樹狀陣列,一直re,後來在發現自己一直忽略離散化。include include using namespace std const int maxn 400000 5 typedef ...

樹狀陣列求逆序對

題目描述 給定乙個陣列a,它包含n個整數,分別是a 1 a 2 a n 如果存在下標i和j,使得 i j 且 a i a j 同時成立,則 i,j 就為乙個 逆序對 那麼a陣列總共有多少對不同的 逆序對 輸入格式 1247.in 第一行為n 1 n 100000 接下來是n行,每行乙個長整型範圍內的...