链接
http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意
给你一个0到n-1,共n个数的一个排列,现在你要做这样一件事,把序列开头的数字拿到序列尾部,你可以做这件事n-1次,问你在这样做的过程中,序列的逆序数最小为多少,输出整个最小逆序数;
题解
对于一个序列来说,它包含0到n-1的数字,而这个序列的第一个元素a[i]来说,它对于整个序列逆序数的贡献是a[i](因为它后面有a[i]个小于它的数,并且它后面有n-a[i]-1个数大于它),那么把它拿到最后面后,则逆序数变化为,x-=a[i],x+=(n-a[i]-1),开始的时候用线段树或者求一下初始序列的逆序数x,之后每次拿都更新x,然后取最小即可;
代码
#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <set>
#include <map>
using namespace std;
#define inf 0x7f7f7f7f
#define maxn 100005
#define mod 1000000007
#define N 5005
#define P 2
typedef long long ll;
typedef struct {
int l, r, sum;
} Tree;
int n, a[N];
typedef struct SEG {
Tree tree[4 * N];
SEG() {
memset(tree, 0, sizeof(tree));
}
void push_up(int x) {
tree[x].sum = tree[x << 1].sum + tree[x << 1 | 1].sum;
}
void build(int x, int l, int r) {
tree[x].l = l, tree[x].r = r;
tree[x].sum = 0;
if (l == r)return;
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
}
int query(int x, int l, int r) {
int res = 0;
if (tree[x].l >= l && tree[x].r <= r) {
return tree[x].sum;
}
int mid = (tree[x].l + tree[x].r) >> 1;
if (l <= mid)res += query(x << 1, l, r);
if (r > mid)res += query(x << 1 | 1, l, r);
return res;
}
void update(int x, int pos, int val) {
int l = tree[x].l, r = tree[x].r;
if (l == r) {
tree[x].sum = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid)update(x << 1, pos, val);
else if (pos > mid)update(x << 1 | 1, pos, val);
push_up(x);
}
} Seg;
int main() {
while (cin >> n) {
for (int i = 1; i <= n; i++) {
cin >> a[i];
a[i];
}
Seg seg;
seg.build(1, 1, n);
int res = 0;
for (int i = 1; i <= n; i++) {
res += seg.query(1, a[i] + 1, n);
seg.update(1, a[i] + 1, 1);
}
int min_res = inf;
for (int i = 1; i <= n; i++) {
res -= a[i];
res += (n - a[i] - 1);
min_res = min(min_res, res);
}
cout << min_res << endl;
}
}