1. 程式人生 > 實用技巧 >P1908 逆序對(樹狀陣列解法)

P1908 逆序對(樹狀陣列解法)

題目描述

貓貓 TOM 和小老鼠 JERRY 最近又較量上了,但是畢竟都是成年人,他們已經不喜歡再玩那種你追我趕的遊戲,現在他們喜歡玩統計。

最近,TOM 老貓查閱到一個人類稱之為“逆序對”的東西,這東西是這樣定義的:對於給定的一段正整數序列,逆序對就是序列中 ai>aja_i>a_jai​>aj​ 且 i<ji<ji<j 的有序對。知道這概念後,他們就比賽誰先算出給定的一段正整數序列中逆序對的數目。注意序列中可能有重複數字。

Update:資料已加強。

輸入格式

第一行,一個數 nnn,表示序列中有 nnn個數。

第二行 nnn 個數,表示給定的序列。序列中每個數字不超過 10910^9109。

輸出格式

輸出序列中逆序對的數目。

輸入輸出樣例

輸入 #1 複製

6

5 4 2 6 3 1

輸出 #1 複製

11

這個題可以用線段樹寫,但樹狀陣列寫起來更優雅一些。資料加強後可以作為樹狀陣列+離散化的板子題了。看到資料範圍我們知道常規的桶肯定是不行的,1e9直接MLE,那麼需要離散化一下。這裡我用的是結構體儲存原數和其初始位置,然後對結構體陣列從小到大sort一遍。此時開闢一個新的陣列rank。比較關鍵的部分是:

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

{

rank[p[i].x]=i;

}

rank陣列也相當一個桶,只不過桶的大小不必為1e9了,只需5e5即可。這樣得到的新的rank陣列實際上和原陣列是等價的,只不過數的絕對大小變成了相對大小。然後利用樹狀陣列處理。但這樣其實有一個問題,就是資料可能是重複的。解決方法有2:一是用stable_sort排序,外加cmp函式裡取等號;二是cmp函式中如果兩個結構體數值相等,那麼按照位置大小排序。這樣的話即使兩個數相等,也會使得分配後的大小能確保不會對逆序數的計算產生影響。

#include <bits/stdc++.h>
using namespace std;
int ans=0,n,i;
int t[500005],rank[500005];
struct point
{
    int num;
    int x;
}p[500005];
bool cmp(point a, point b)
{
    if(a.num != b.num) return a.num < b.num;
    return a.x < b.x;
}
void add(int x, int y)
{
    for( ; x <= n; x += x & (-x)) t[x] += y;
}
int ask(int x) { int ans=0; for(; x; x -= x & -x) ans += t[x]; return ans; } int main() { cin>>n; int i; long long cnt=0; for(i=1;i<=n;i++) { scanf("%d",&p[i].num); p[i].x=i;//位置 } sort(p + 1, p + n + 1, cmp);//按照原數排序 for(i=1;i<=n;i++) { rank[p[i].x]=i; } for(i=1;i<=n;i++) { cnt += 0ll + ask(n) - ask(rank[i]); add(rank[i], 1); } cout<<cnt; return 0; }