Minimum Inversion Number(线段树&&逆序对)

原题链接
Problem Description
The inversion number of a given number sequence a1, a2, …, an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, …, an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, …, an-1, an (where m = 0 - the initial seqence)
a2, a3, …, an, a1 (where m = 1)
a3, a4, …, an, a1, a2 (where m = 2)

an, a1, a2, …, an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.

Input
The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.

Output
For each case, output the minimum inversion number on a single line.

Sample Input
10
1 3 6 9 0 8 5 7 4 2

Sample Output
16

Author
CHEN, Gaoli

//http://acm.hdu.edu.cn/showproblem.php?pid=1394
/*
题意:给一个0-n-1的排列,这个排列中的逆序数为数对 (ai, aj) 满足
 i < j and ai > aj的个数。依次把第一个数放到排列的末尾会得到另外n-1个排列,
 求这n个排列中的最小的逆序数。

如果是0到n的排列,那么如果把第一个数放到最后,对于这个数列,
逆序数是减少a[i],而增加n-1-a[i]的.当然,你也可以是统计小于这个数的有多少个,
然后再用已经插入树中i个元素减去小于这个数的个数,得出的结果也是在一样的
//对于1 3 6 9 0 8 5 7 4 2这个数字串来说,逆序对就是
//   0 0 0 0 4 1 3 2 5 7,也就是对某一个数来说,前面有多少个数比他大他的逆序数就是多少
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define lson (id<<1)//这两个用法还是相当好用的
#define rson ((id<<1)|1)
//#define mid ((l+r)>>1)
using namespace std;
const int maxn=5000 + 5;
struct node{
        int l,r;
        int num,MAX;
}tree[(maxn<<2)];//这里的数组建为原来的四倍是有理论依据的
int a[maxn];
//为根节点为id的树根建树
void create(int id,int l,int r){
        tree[id].l=l;tree[id].r=r;
        tree[id].num=0;//由于这个题的限制所以这里要初始化为0
        if(l==r) return;
        int mid=(l+r)/2;
        create(lson,l,mid);
        create(rson,mid+1,r);
        return;
}
//找到结点id所代表的区间的和
int getsum(int id,int l,int r){
        if(l<=tree[id].l && tree[id].r<=r) return tree[id].num;
        int sum=0;
        int mid=(tree[id].l+tree[id].r)/2;
        if(l<=mid)
                sum+=getsum(lson,l,r);
        if(r>mid)
                sum+=getsum(rson,l,r);
        return sum;
}
//更新这个num的结点的值(在本题中是要求更新为1的)
void updata(int id,int num){
        if(tree[id].l==num && tree[id].r==num){
                tree[id].num=1;
                return;
        }
        int mid=(tree[id].l+tree[id].r)/2;
        if(num<=mid)
                updata(lson,num);
        else
                updata(rson,num);
        tree[id].num=tree[lson].num + tree[rson].num;
        return;
}
int main(){
        int n;
        while(scanf("%d",&n)==1&&n!=0){
                create(1,0,n-1);
                memset(a,0,sizeof(a));
                int ans=0;
                for(int i=0;i<n;i++){
                        scanf("%d",&a[i]);
                        ans+=getsum(1,a[i]+1,n-1);//得到前面有多少个数比他大
                        updata(1,a[i]);//然后这个数既然已经统计了,当然应该将这个数字放下,然后让后面的数字来计算逆序对的数中是否算上他
                }
                int minx=ans;
                for(int i=0;i<n;i++){
                        ans=ans+n-2*a[i]-1;//这两个连续的序列之间存在着这样的一个关系
                        minx=min(minx,ans);
                }
                printf("%d\n",minx);
        }
        return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

门豪杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值