题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394
题目大意:给定一个序列,求逆序数总和,也就是在当前位置后面比当前值小的数的个数。这题的序列,前后可以滑动即第一个可能变到最后面。
解题思路:换个角度找逆序数,即在当前位置之前比当前值大的数的个数,那只要在每次输入一个数的时候寻找x+1...n-1里出现过的个数。那关键问题就是如果快速地查找到逆序数,如果暴力,n^2,也可以过。但考虑用线段树查询,其实就是很普通的线段树。关于线段树,可以去傻崽的空间膜拜下www.notonlysuccess.com.然后找最小的逆序数总和,则可以一个个往后挪,tot = tot + (n - arr[i]) - (arr[i] - 1),ans = min(tot).
测试数据:
5
5 4 3 2 1
6
1 0 2 3 4 5
代码:
#include <stdio.h>
#include <string.h>
#define MAX 6000
#define lson l,m,rt << 1
#define rson m+1,r,rt << 1 | 1
#define min(a,b) (a) < (b) ? (a) : (b)
#define max(a,b) (a) > (b) ? (a) : (b)
int sum[MAX*4],n;
int tot,arr[MAX],cnt;
void PushUp(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
int query(int L,int R,int l,int r,int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int m = (l + r) >> 1,ret = 0;
if (L <= m) ret += query(L,R,lson);
if (R > m) ret += query(L,R,rson);
return ret;
}
void update(int p,int l,int r,int rt) {
if (l == r) {
sum[rt]++;
return ;
}
int m = (l + r) >> 1;
if (p <= m) update(p,lson);
else update(p,rson);
PushUp(rt);
}
int main() {
int i,j,k,t;
while (scanf("%d",&n) != EOF) {
tot = 0;
for (i = 0; i <= 4 * n + 1; ++i)
sum[i] = 0;
for (i = 0; i < n; ++i) {
scanf("%d",&arr[i]);
tot += query(arr[i],n-1,0,n-1,1);
update(arr[i],0,n-1,1);
}
int ret = tot;
for (i = 0; i < n; ++i) {
tot += n - arr[i] - 1 - arr[i];
ret = min(tot,ret);
}
printf("%d\n",ret);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。