HDU 1394:Minimum Inversion Number

点击打开题目链接

题目意思是给出一个数n,然后给出0~n-1中不同的n的个数组成一个序列,然后每次将最后一个数放到最后一个,然后看这样一次次转换的序列中的

逆序树的最小值。

求逆序树有好多种方法:暴力法,归并排序法,树状数组法,线段树法。最近学习线段树,所以就用线段树写,其他方法后续会补上。

这里对于题目所给序列。对于第一个数x,则后面必然有x-1~0这几个数都比它小,则逆序对为x,后面的x+1~n-1都比x大是正序的,无影响。

当吧x挪到最后一位时,本来x-1~0这几个数是在x后面,则现在变成是在前面,则当前逆序数减去x对,原来的x+1~n-1都再x的前面了,则

逆序数对又增加了n-1-x个,假如当前序列逆序数为num,则将第一位x挪到最后一位,序列的逆序数变为 num - x + n - 1 - x。

则序列变换时,求逆序数的时间复杂度为线性阶O(n)。时间复杂度已经不能再减少了,这时可以通过线段树来优化求原始序列的逆序数的

时间复杂度,首先构建一个空的线段树,里面不含有任何元素,当输入一个数x[i]时,现在线段树中查找比x大的元素的个数,当前在线段

树中的元素其在序列中的下标都比x[i]对应的下标小,则查找出的元素个数,是数x[i]的逆序对数量,然后再将新元素插入线段树。


AC代码:

#include<iostream>
#include<stdio.h>
#define lchild left,mid,root<<1
#define rchild mid+1,right,root<<1|1

using namespace std;

const int maxn = 5010;
/**使用线段树求逆序树**/
int sum[maxn<<2];
int x[maxn];

void push_up(int root)
{
    sum[root] = sum[root<<1] + sum[root<<1|1];
}
void build(int left,int right,int root)
{
    sum[root] = 0;   //最开始每个节点sum都是0
    if(left == right)
    {
        return;
    }
    int mid = (left+right)>>1;
    build(lchild);
    build(rchild);
}
//将新输入的树插入线段树
void Insert(int x,int left,int right,int root)
{
    if(left==right)
    {
        sum[root]++;
        return;
    }
    int mid = (left+right)>>1;
    if(x<=mid) Insert(x,lchild);   //插入左子树
    else Insert(x,rchild);         //插入右子树
    push_up(root);
}
//当输入一个数x时,查找线段树中x~n-1范围中数的个数
int query(int L,int R,int left,int right,int root)
{
    if(L<=left && right<=R)
    {
        return sum[root];
    }
    int ans = 0;
    int mid = (left+right)>>1;
    if(L<=mid) ans += query(L,R,lchild);
    if(R>mid) ans += query(L,R,rchild);
    return ans;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int num = 0;
        build(0,n-1,1);  ///构建空的线段树
        ///用线段树求逆序数
        for(int i = 0; i < n; i++)
        {
            scanf("%d",&x[i]);
            num += query(x[i],n-1,0,n-1,1);
            Insert(x[i],0,n-1,1);
        }
        int ans = num;
        for(int i = 0; i < n; i++)
        {
            num = num+n-1-2*x[i];
            ans = min(ans,num);
        }
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值