题目大意
给定一个排列数列 可以将其前面m个数移到后面得到一个新序列 问移动后所得的最小逆序对数量为多少
思路
线段树求逆序对经典题 每次输入w[i] 都将其放入线段树中 然后更新一下区间和sum 再query一下当前在w[i]输入前输入的数比w[i]大的数量为多少 即区间[w[i]+1, n]的当前sum为多少 最后加起来即初始序列逆序对 再从1到n遍历 每次求当前逆序对-(w[i] - 1)+n-w[i] 显然这是更新后的序列的逆序对数量 求最小值即可
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 5010, INF = 1e8;
int n;
int w[N];
struct Node
{
int l, r;
int sum;
}tr[N * 4];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r)
{
tr[u] = {l, r, 0};
if (l != r)
{
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
}
void modify(int u, int x)
{
if (tr[u].l == x && tr[u].r == x) tr[u].sum ++ ;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x);
else modify(u << 1 | 1, x);
pushup(u);
}
}
int query(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
else
{
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if (l <= mid) sum = query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
}
int main()
{
while (~scanf("%d", &n))
{
build (1, 1, n);
int cnt = 0, ans = INF;
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &w[i]);
w[i] ++ ;
modify(1, w[i]);
cnt += query(1, w[i] + 1, n);
}
ans = cnt;
for (int i = 1; i < n; i ++ )
{
cnt = cnt - (w[i] - 1) + n - w[i];
ans = min(ans, cnt);
}
printf("%d\n", ans);
}
return 0;
}