http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意:给定无序的n个数 0~n-1,可以这样变幻得到n的不同的序列:每次将序列中第一个数放到最后一个。
问在这n个序列中逆序对数最少是多少?
思路:
已知逆序对数的定义后,可以直接暴力求原序列的逆序对数,设为sum;可以发现以后n-1个的序列的逆序对数就知道了,不难证明,两个相邻序列的逆序对数差值为 n-1-2*x。
线段树也是直接模拟这个过程。每扫描到a[i],先去查询[a[i]+1,n-1]区间的数的个数,就是a[i]的逆序对数,然后更新a[i]所在区间,将这些逆序对数累加。
首先建树,节点中有一个num域,如果某个数在该区间中,那么该区间的num值加1。相当于数x在线段树中走一遍,用num来记录它的足迹(个人理解)。每输入一个数x就查询在区间[x+1,n-1]中的num值,也就是在输入x之前比x大的数的个数,正好对应逆序对数的定义。然后再把x插入线段树中。这样就把原序列的逆序对数求出来了。
#include <stdio.h>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <math.h>
#include <string.h>
#include <queue>
#include <string>
#include <stdlib.h>
#include <algorithm>
//#define LL long long
#define LL __int64
#define eps 1e-12
#define PI acos(-1.0)
using namespace std;
const int INF = 0x3f3f3f3f;
const int maxn = 5010;
struct node
{
int l,r;
int num;
}tree[maxn*4];
int a[maxn];
void build(int v, int l, int r)
{
tree[v].l = l;
tree[v].r = r;
tree[v].num = 0;
if(l == r)
return;
int mid = (l+r) >> 1;
build(v*2,l,mid);
build(v*2+1,mid+1,r);
}
int query(int v, int l, int r)
{
if(tree[v].l == l && tree[v].r == r)
return tree[v].num;
int mid = (tree[v].l + tree[v].r) >> 1;
if(r <= mid)
return query(v*2,l,r);
else if(l > mid)
return query(v*2+1,l,r);
else return query(v*2,l,mid) + query(v*2+1,mid+1,r);
}
void update(int v, int x)
{
if(tree[v].l <= x && tree[v].r >= x)
tree[v].num++;
if(tree[v].l == tree[v].r)
return;
int mid = (tree[v].l + tree[v].r) >> 1;
if(x <= mid)
update(v*2,x);
else
update(v*2+1,x);
}
int main()
{
int n;
int ans;
while(~scanf("%d",&n))
{
build(1,0,n-1);
ans = 0;
for(int i = 0; i < n; i++)
{
scanf("%d",&a[i]);
if(i != 0 && a[i] != n-1)
ans += query(1,a[i]+1,n-1);
update(1,a[i]);
}
int res = ans;
for(int i = 0; i < n; i++)
{
ans += n-1-2*a[i];
if(res > ans)
res = ans;
}
printf("%d\n",res);
}
return 0;
}