HDU 1394 线段树求逆序数

http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目链接,这题想了一段时间。关键是数据是0-n-1这个突破口,利用我们插入线段树的先后就可以很快求出来有多少个逆序对了。
例如:
4
2 1 3 4 这个案例,
我按顺序肯定先插入2的,这时候我线段树L=R=2这个点就置为1,然后往上面更新啊。于是个区间个数加一。
然后问题来了,我插入1,这时候按照逆序对的定义我先找比他大的,还要比他之前插入的(这里涉及到一个细节,代码给出)于是,很简单,查询比他大的数字的区间有多少个数字,于是我查询2-4区间,发现有一个1,于是我逆序数就+1;后面的也是这样操作。(题目数字是0-N-1)
然后这题目还有个地方就是往后面移动数字,这个根据手推,原来第一的数字被放到尾巴了,就需要考虑
1.应为是头,前面没数字,于是我们可以得出一个结论,放在尾巴后面之后,之前比他大的数字全部与他自己构成逆序对,又因为,数字区间是连续的,而且重0开始,所以,会增加n-1-NUM[i](num是放输入的数组)
2.之前比他小的,现在已经不是逆序数了,有多少个呢?也应为是头,而且连续,所以有NUM[I]个。。。我们减去他
于是,综合2点,就有
ans = ans + n - 2 * num[i] - 1;

#include<iostream>
#include<algorithm>
using namespace std;
int tree[100000];
int num[1000000];
void updata(int x,int l,int r,int k)
{
    if (l == r)
    {
        tree[k] = 1;
        return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid)
        updata(x, l, mid, k << 1);
    else
        updata(x, mid + 1, r, k << 1|1);
    tree[k] = tree[k << 1] + tree[k << 1 | 1];
}
int qury(int x, int y, int l, int r, int k)
{

    if (l == x&&r == y)
    {
        return  tree[k];
    }
    int mid = (l + r) >> 1;
    if (y <= mid)
    {
        return qury(x, y, l, mid, k << 1);
    }
    else if (x > mid)
    {
        return qury(x, y, mid + 1, r, k << 1 | 1);
    }
    else
        return qury(x, mid, l, mid, k <<1) + qury(mid + 1, y, mid + 1, r, k << 1 | 1);
}
int main()
{
    int n;
    while (cin >> n)
    {
        memset(num, 0, sizeof(num));
        memset(tree, 0, sizeof(tree));
        int ans = 0;
        for (int i = 1; i <= n; i++)
        {
            cin >> num[i];
            ans += qury(num[i], n - 1, 0, n - 1, 1);//细节,如果放下面一层,最后一个数字假如是最大的,导致会多出几个数,而这个数是最后的,所以是不算的
            updata(num[i], 0, n - 1, 1);

        }
        int min = ans;
        for (int i = 1; i <= n; i++)
        {
            ans = ans + n - 2 * num[i] - 1;

            if (min > ans)
                min = ans;
        }
        cout << min << endl;
    }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值