[线段树总结题单]

题单链接
持续更新

单点更新

1、Minimum Inversion Number

题意
给了一个大小为n的数组,每个数的大小在0 - n-1之间,现在你可以执行n次操作,每一次将数组中的第一个数移动到最后一位,每执行一次操作,寻找当前数组的逆序对的大小,n次操作执行完后,让你找逆序数的最小数量是多少(逆序数:前面比它大的数的个数)。
思路
线段树
首先数组中的数都是1-n的(我们把每个数都+1),那么我们使用线段树来求逆序对,只需要每次将a[i]这个这个位置+1,然后求a[i]后面有多少个数是比它小的,这样就求到了逆序对的数目。(如果a[i]的范围不是1-n的话,我们可以进行离散化在求)。
接下来思考移动了第一个数后逆序对的变化:假设在x这个位置,x-n中比它大的数为m个,那么比它小的数有n-m-1个,那么当它移动到末尾的时候,之前比它大的数会和它构成逆序对,那么逆序对的数目会增加,但是之前比它小的,在移动之前跟它构成逆序对,移动了之后逆序对会减少,所以移动之后的逆序对数目是(cnt表示初始的逆序对数目):cnt - (n-m-1) + m;

#include <bits/stdc++.h>

#define ul u << 1
#define ur u << 1 | 1

using namespace std;

const int mod = 1e9 + 7, N = 1e5 + 10;

int n;
int a[N];
struct node
{
    int l, r;
    int sum;
}tr[N << 2];

void pushup(int u) { tr[u].sum = tr[ul].sum + tr[ur].sum; }

void build(int u, int l, int r)
{
    tr[u] = {l, r, 0};
    if (l == r) return ;

    int mid = l + r >> 1;
    build(ul, l, mid), build(ur, mid + 1, r);
}

void modify(int u, int pos)
{
    if (tr[u].l == pos && tr[u].r == pos) { tr[u].sum = 1; return; }

    int mid = tr[u].l + tr[u].r >> 1;
    if (pos <= mid) modify(ul, pos);
    else modify(ur, pos);

    pushup(u);
}

int query(int u, int l, int r)///查询l-r之间有多少个数是比它小的。
{
    if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum;

    int ans = 0, mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) ans = query(ul, l, r);
    if (r > mid) ans += query(ur, l, r);

    return ans;
}

void solve()
{
    int ans = 0x3f3f3f3f, res = 0;
    build(1, 1, n);

    for (int i = 1; i <= n; i ++)
    {
        scanf("%d", &a[i]); a[i] += 1;
        modify(1, a[i]);///在a[i]这个位置加上1。

        ///每次修改了之后,查询这个数后面有多少个数是比它小的,这样就找到了逆序对
        int cnt = query(1, a[i] + 1, n);
        res += cnt;///求到原始顺序的逆序对
    }

    for (int i = 1; i <= n; i ++)
    {
        res = res - (a[i] - 1) + n - a[i];
        ans = min(ans, res);
    }

    printf("%d\n", ans);
}

signed main()
{
    while (~scanf("%d", &n)) solve();

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值