首页 > 代码库 > HDU - 1394 Minimum Inversion Number(线段树求逆序数---点修改)

HDU - 1394 Minimum Inversion Number(线段树求逆序数---点修改)

题意:给定一个序列,求分别将前m个数移到序列最后所得到的序列中,最小的逆序数。

分析:m范围为1~n,可得n个序列,求n个序列中最小的逆序数。

1、将序列从头到尾扫一遍,用query求每个数字之前有多少个大于该数字的数,方法如下。

(1)将已经扫过的数字所对应的位置标记,通过query求该数字之后有多少个数被标记过

(2)该数字之后所有被标记的数字,都是在该数字之前出现过的(i<j),而这些数字又大于该数字(ai>aj),因此该数字之后所有的标记和就是该数字之前比该数字大的数的个数。

2、sum为初始序列的逆序和,从前往后依次将每个数字移到序列最后。

假设当前把a[i]移到序列最后,则

后一个序列B的逆序和=前一个序列A的逆序和 - a[i] + (n - 1 - a[i])。

eg:假设前一个序列A为3 6 9 0 8 5 7 4 2 1,则a[i]为3。

a[i]是序列A的第一个数,a[i]后面的数中有a[i]个数小于a[i](分别为0,2,1),因此把a[i]移到最后,会减少a[i]个逆序数。

而a[i]后面的数中有 (n - 1 - a[i])个数大于a[i],因此把a[i]移到最后,会增加 (n - 1 - a[i])个逆序数。

#include<cstdio>#include<cstring>#include<cstdlib>#include<cctype>#include<cmath>#include<iostream>#include<sstream>#include<iterator>#include<algorithm>#include<string>#include<vector>#include<set>#include<map>#include<stack>#include<deque>#include<queue>#include<list>#define lowbit(x) (x & (-x))const double eps = 1e-8;inline int dcmp(double a, double b){    if(fabs(a - b) < eps) return 0;    return a > b ? 1 : -1;}typedef long long LL;typedef unsigned long long ULL;const int INT_INF = 0x3f3f3f3f;const int INT_M_INF = 0x7f7f7f7f;const LL LL_INF = 0x3f3f3f3f3f3f3f3f;const LL LL_M_INF = 0x7f7f7f7f7f7f7f7f;const int dr[] = {0, 0, -1, 1, -1, -1, 1, 1};const int dc[] = {-1, 1, 0, 0, -1, 1, -1, 1};const int MOD = 1e9 + 7;const double pi = acos(-1.0);const int MAXN = 5000 + 10;const int MAXT = 10000 + 10;using namespace std;int a[MAXN];int cnt[MAXN << 2];void update(int k, int id, int L, int R){    if(L == R){        ++cnt[id];    }    else{        int mid = L + (R - L) / 2;        if(k <= mid) update(k, id << 1, L, mid);        else update(k, id << 1 | 1, mid + 1, R);        cnt[id] = cnt[id << 1] + cnt[id << 1 | 1];    }}int query(int l, int r, int id, int L, int R){    if(l <= L && R <= r){        return cnt[id];    }    int mid = L + (R - L) / 2;    int ans = 0;    if(l <= mid) ans += query(l, r, id << 1, L, mid);    if(r > mid) ans += query(l, r, id << 1 | 1, mid + 1, R);    return ans;}int main(){    int n;    while(scanf("%d", &n) == 1){        memset(cnt, 0, sizeof cnt);        int sum = 0;        for(int i = 0; i < n; ++i){            scanf("%d", &a[i]);            sum += query(a[i] + 1, n, 1, 1, n);            update(a[i] + 1, 1, 1, n);        }        int ans = sum;        for(int i = 0; i < n; ++i){            sum = sum - a[i] + (n - 1 - a[i]);            ans = min(ans, sum);        }        printf("%d\n", ans);    }    return 0;}

 

HDU - 1394 Minimum Inversion Number(线段树求逆序数---点修改)