1. 程式人生 > >HDU 4911 (樹狀陣列求逆序數+離散化)

HDU 4911 (樹狀陣列求逆序數+離散化)

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=4911

題意:最多可以交換K次,就最小逆序對數。

思路:逆序數定理,當逆序對數大於0時,若ai<ai+1,那麼交換後逆序對數+1,反之-1。所以只需要求一下逆序數的個數就行了。逆序數的求解可以用樹狀陣列。我們用樹狀陣列維護某個區間中數字出現的個數,將原資料按其原來順序插入樹狀陣列,第i個數字插入的方式為將樹狀陣列的第a[i]位設為1,同時更新覆蓋到它的父區間,sum(a[i])可求得[1, a[i]]的區間和,這恰好代表第i個數字前小於等於它的個數,等於的只可能是自身,故小於它的有sum(a[i])-1個,那麼大於它的顯然就有i-1-(sum(a[i])-1) = i-sum(a[i])個。

要知道樹狀陣列的長度是資料範圍,由於資料比較大,所以要離散化。注意開long long。

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
#define ll long long
const int maxn = 1e5+5;
int n, k;
bool cmp(pii a, pii b)
{
    return a.first < b.first;
}
struct BIT
{
    int n, c[maxn*5];
    void init(int n)
    {
        this -> n = n;
        mem(c,0);
    }
    void add(int p, int x)
    {
        for(int i = p; i <= n; i += i&-i)
            c[i] += x;
    }
    ll sum(int p)
    {
        ll ans = 0;
        for(int i = p; i >= 1; i -= i&-i)
            ans += c[i];
        return ans;
    }
}bit;
int main()
{
    while(~scanf("%d%d", &n,&k))
    {
        bit.init(n); int x; ll cnt = 0;
        vector <pii> v; map <ll,ll> mp;
        v.clear(); mp.clear();
        for(int i = 0; i < n; i++)
        {
            scanf("%d",&x);
            v.push_back({x,i+1});
        }
        sort(v.begin(),v.end(),cmp);
        int c = 1; mp[v[0].second] = c;
        for(int i = 1; i <= n; i++)
        {
            if(v[i].first == v[i-1].first) mp[v[i].second] = c;
            else mp[v[i].second] = ++c;
        }
        for(int i = 1; i <= n; i++)
        {
            bit.add(mp[i],1);
            cnt += i - bit.sum(mp[i]);
        }
        printf("%lld\n",max(cnt-k,0LL));
    }
}