求逆序對(樹狀陣列,線段樹和歸併)

2021-09-17 22:52:34 字數 3458 閱讀 9909

樹狀陣列求逆序對,歸併排序求逆序對。

一 樹狀陣列

我看了這篇部落格,才逐漸明白的,樹狀陣列求逆序對的一種思路。

①桶排序,把陣列a的元素放在乙個很大的陣列b裡面,(確保a的元素最大值不超過b的長度)

挨個把a中的元素放到b中去,,就是令b[a[i]]=1,每次求已經放入的元素個數減去a[i]前面的元素個數(包括a[i])

用樹狀陣列的單點修改,區間求和可以很好地完成。(手動寫**ing)

#include#include#include#includeusing namespace std;

int a[500005],b[500005],n;

int lowbit(int x)

void add(int x)

}int getsum(int x)

return sum;

}int main()

cout比如5個數,9 ,1,2,7,5。先把b[9]置為1,此時9前面沒有數,再把b[1]置為1,此時1前面沒有數(比一小的數),並且已經插入了兩個數,那麼就說明a這個序列在1的前面有乙個比一大的數,即有一對逆序數。

再把b[2]置為1,二的前面有乙個比自己小的數1,此時已經插入了三個數,那麼就說明a這個序列中,有乙個比2大的數,即有一對逆序數。以此類推,就全部求出來了。

②前面的方法很簡單,但是限制實在太多,比如只能求正整數的逆序對(b的下標不能為負數),如果a中的元素最大值很大,就會超時。

那麼就需要進行離散化。這玩意實在有點難理解。

首先,需要把a設定為結構體陣列,結構體儲存的值為元素大小,及下標。

然後將a按照元素大小用sort函式排序,記得寫自定義的排序方式,還有就是因為a的下標從1開始,所以sort(a+1,a+n+1,cmp)。

不是sort(a,a+n,cmp)。

這裡有兩種方法去做,雖然**差不多,但是我個人認為有很大的不同。

第一種我看這部落格做的。

#include#include#include#includeusing namespace std;

int b[500005],n,t[500005];

struct nodea[500005];

bool cmp(node x,node y)

return sum;

}int main()

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

cnt=1;

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

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

cout這種方法的思想和前面的是一樣的,按照順序依次插入,並且求前面有多少個數,再用已插入的數的個數減去。

cnt的作用就是避免有大小相同的元素。pos代表的是a中元素插入的順序,比如還是那五個數9,1,2,7,5。

9的pos是1,1的pos是2,5的pos是5。按照a中元素大小排序之後,9在最後一位,1在第一位,5在第三位。

此時cnt代表的是元素大小關係,9對應的cnt為5,1對應的cnt為1,5對應的cnt為3。那麼此時的pos所對應的數的相對大小依然沒有變(pos是原來的序列的順序)比如pos為1的數是9,是這個序列中最大的數。排序後,a的序列v值為1 2 5 7 9;(最重要的是pos沒有變,1這個數對應的pos依然是2,因為它是原來的序列的第二個數,9對應的pos依然是1,也就是說,pos為2的數是1,

pos為1的數是9)t[a[i].pos]=cnt;在這個操作後,pos所對應的數的相對大小沒有變化。pos為1的cnt為5(9是最大的),pos為2cnt為1(1是最小的)pos為5的cnt為3(5是第三大的)

最後,在呼叫add()函式的時候,i為1實際上是a[i].pos為1,也就是說,這樣是按照原來的順序插入的。這是模擬前面所講的方法。

第二種#include#include#include#includeusing namespace std;

int b[500005],n,t[500005];

struct nodea[500005];

bool cmp(node x,node y)

return sum;

}int main()

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

/*cnt=1;

for(i=1;i<=n;i++) 將這一段刪去*/

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

cout這樣做的意思是先排序,然後按照從小到大的順序插入pos。這樣的話,每次插入的數一定比之前插入的大,把b[a[i].pos]置為1,a[i].pos之前的數就是原序列中比a[i].v小的數,用已經插入的數的個數減去,就是逆序對的個數。

二 歸併排序

歸併排序求逆序對需要注意的就是因為兩部分已經排好序了,每次加的應該是mid-i+1;手動模擬一遍就懂了。

#include#include#include#include#include#includeusing namespace std;

long long a[100005],cnt;

void merge(int l,int mid,int r)//直到mid肯定都比a[j]大

else

} while(i<=mid)

while(j<=r)

for(i=l,k=0;i<=r;i++)

}void mergeup(int l,int r)

int main()

mergeup(0,n-1);

cout三:線段樹

和樹狀陣列一樣,但是是靠線段樹來單點修改,區間求和

#include#include#include#include#include#includeusing namespace std;

int sum;

struct node;

node tree[400005];

struct te;

te a[100005];

void build(int left,int right,int k)

int mid=(tree[k].l+tree[k].r)/2;

build(left,mid,2*k);

build(mid+1,right,2*k+1);

}void getsum(int ll,int rr,int k)

int mid=(tree[k].l+tree[k].r)/2;

if(mid>=ll)getsum(ll,rr,2*k);//>=ll

if(midmid)change(2*k+1,x);

tree[k].v=tree[2*k].v+tree[2*k+1].v;

}bool cmp(te x,te y)

int main()

ans=0;

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

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

cout<

} return 0;

}

逆序對(樹狀陣列 歸併)

題目描述 貓貓tom和小老鼠jerry最近又較量上了,但是畢竟都是成年人,他們已經不喜歡再玩那種你追我趕的遊戲,現在他們喜歡玩統計。最近,tom老貓查閱到乙個人類稱之為 逆序對 的東西,這東西是這樣定義的 對於給定的一段正整數序列,逆序對就是序列中i小於j同時ai大於aj的有序對。知道這概念後,他們...

求逆序對(歸併排序 樹狀陣列)

兩種演算法的時間複雜度都是 o nlogn 但是,有可能樹狀陣列需要離散化!所以,由許多元素共同影響下,歸併排序求逆序對 比 樹狀陣列求逆序對 歸併排序 include define ll long long define n 100005 using namespace std int a n t...

線段樹或樹狀陣列求逆序數

線段樹或樹狀陣列求逆序數 求逆序數的方法有分治,歸併,本文只介紹線段樹或樹狀陣列求逆序數的辦法,眾所周知,線段樹和樹狀樹可以用來解決區間操作問題,就是因為這兩個演算法區間操作的時間複雜度很低o logn 才讓這種方法具有可行性。首先先來看乙個序列 6 1 2 7 3 4 8 5,此序列的逆序數為5 ...