线段树求解Minimum Inversion Number

题:Minimum Inversion Number

题意:给出一个序列,如果某一项比它前面的项小(本来应该是一次增大的),这就是一组逆序项,如例:1 3 6 9 0 8 5 7 4 2就有22组逆序项。(1,0)(3,0)(3,2)(6,0)(6,5)(6,4)(9,0)(9,8)(9,5)。。。现在你可以将x1移至最后,再将x2移至最后,直到把xn-1移到最后(xn移到最后无意义),在这过程中,逆序项数目最少为多少?

分析:思路是先求出原序列的逆序项数目,再通过递推求出所有情况下逆序项数目。

先建立一棵空树,每段的sum都为0,输入每一项(如x1)时可查询树中已存在且比它大的数的个数(因为再它之前输入的是它前面的项),即这一项的逆序项的个数,再将这一项插入树中,更新树(为下次查询),一直到输入结束,此时累加起来的和即是这个序列的逆序项个数总和。然后递推求将所有情况下逆序项。记m为当前(原)序列的逆序项个数,将第一项x1移至最后时,此时逆序项个数为m-x1+((n-1)-x1),假设x1等于0,将0移到序列最后是原来的逆序项减去0(因为后面没有比0小的项),再加上((n-1)-0)(因为0放在最后时,0前面(n-1)个数都是逆序数),同理x2、x3、x4。。。

代码:

#include<cstdio>
#include<algorithm>
#define lson l, m, rt<<1
#define rson m + 1, r, rt<<1|1
using namespace std;

const int maxn = 5555;
int sum[maxn<<2];
int x[maxn];

void PushUP(int rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
/********************************
建树,初始化sum值全为0,没有节点
*********************************/
void build(int l ,int r, int rt){
    sum[rt] = 0;
    if(l == r) return;
    int m = (r + l)>>1;
    build(lson);
    build(rson);
}
/******************************************
插入节点p,更新所有与p有关的sum
******************************************/
void update(int p, int l, int r, int rt){
    if(l == r){
        sum[rt]++;
        return;
    }
    int m = (l + r)>>1;
    if(p <= m) update(p, lson);
    else update(p, rson);
    PushUP(rt);
}
/*******************************************
查询L到R之间存在节点的个数
题意是查询大于L的数的个数,即默认R为n-1
*******************************************/
int query(int L, int R, int l, int r, int rt){
    if(L <= l && r <= R) return sum[rt];
    int m = (l + r)>>1;
    int ret = 0;
    if(L <= m) ret += query(L, R, lson);
    if(R > m) ret += query(L, R, rson);
    return ret;
}

int main(){
    int n;
    while(~scanf("%d", &n)){
        build(0, n - 1, 1);
        int m = 0;
        for(int i = 0; i < n; i++){
            scanf("%d", &x[i]);
            m += query(x[i], n - 1, 0, n - 1, 1);//计算初始序列所有逆序数的个数m
            update(x[i], 0, n - 1, 1);
        }
        int ans = m;
        for(int i = 0; i < n; i++){
            m += ((n - 1) - x[i]) - x[i];//当把x[i]移至最后,m会减少x[i],会增加((n - 1) - x[i])
            ans = min(ans, m);
        }
        printf("%d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值