天天看點

hdu 6318 Swaps and Inversions(樹狀數組+離散化)

題目連結

題意:有一個長度為n的亂序集合,如果一個子序列為逆序,你就要付x元,或者你可以選擇修改一個相鄰的數字,你就要付y元,問最少你要付多少錢。(其實你交換一個存在逆序的相鄰數字就可以減少一個逆序數)

這題不知道是題目的意思太繞還是怎樣,我看了很久硬是每看懂題目在講什麼,後來看了一下大佬們的代碼和解釋才知道,這不就是樹狀數組的入門題嗎。。我。。。英語和國文閱讀能力大概是沒救了。。。

思路:就是算這個集合存在多少逆序數,最後的答案乘min(x,y)就可以了。(好像應該也可以用線段樹做,不過這個就是數組數組的闆子題)

AC代碼:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long ll;
const int maxn = 1e5+10;
int c[maxn],a[maxn],b[maxn];
int n, x, y;

ll lowbit(int x){
    return x&(-x);
}

void add(int x,int y){
    for(int i = x; i <= n; i+=lowbit(i))
        c[i] += y;
}

ll query(int x){
    ll ans = 0;
    for(int i = x; i > 0; i-=lowbit(i))
        ans += c[i];
    return ans;
}

int main(){
    int i;
    while(scanf("%d%d%d",&n,&x,&y)!=EOF){
        memset(c, 0, sizeof(c));
        for(i = 1; i <= n; i++){
            scanf("%d",&a[i]);
            b[i] = a[i];
        }
        sort(b+1, b+1+n);
        ll ans = 0;
        for(i = n; i > 0; i--){      //這裡要從n開始,因為可能存在相同的數字,把數字按原本位置從後往前插入樹狀數組,檢視在它原本位置後面有多少個小于它的數,就存在多少逆序數
            int t = lower_bound(b+1, b+1+n, a[i])-b;    //找到第一個大于等于a[i]的數的位置,是以查詢的時候要減1
            ans += query(t-1);
            add(t, 1);
        }
        printf("%lld\n",ans*min(x,y));
    }
    return 0;
}