标签:
题意: 输入0 ~ n-1 总共n个数,每次取最前面的一个数放到序列最后面形成新的序列,问这些序列中逆序对数目最少的是多少。
如: 输入0,3,2,4,1,5
先求逆序对数目,第一次将0放到最后 3,2,4,1,5,0 再求逆序对数目,依次下去,取逆序对数目最小值。
用线段树求最初输入的序列的逆序对。
求逆序对有两种思想,一是:求a[i]后面比它小的数的个数,累加。二是:求a[i]前面比它大的数的个数,累加。线段树用的是第二种思想
每次将a[i]插入,就将区间为[i,i]的叶子结点值置1.
如我们求[a[i], n-1]区间内的1的个数,就是求a[i]前面比a[i]大的数的个数,因为比a[i]大的数已经插入并且在[a[i], n-1]内。
每次移动最前面的数到最后面,逆序对数目变化是 - a[i] + (n - 1) - a[i]
因为a[i]后面有a[i]个比它小的数,有(n - 1) - a[i]个比它大的数,所以每次移动,逆序对减少a[i] ,增加(n - 1) - a[i].
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 5050;
int num[MAXN << 2];
void PushUp(int rt)
{
num[rt] = num[rt << 1] + num[rt << 1 | 1];
}
void build(int l, int r , int rt)
{
if(l == r)
{
num[rt] = 0; //建树叶子结点值置0
return;
}
int mid = ( l + r) >> 1;
build( l, mid, rt << 1);
build(mid + 1, r, (rt << 1) +1 );
PushUp(rt);
}
void update(int p, int l, int r, int rt)
{
if(l == r)
{
num[rt] ++; //插入一个数,将其对应的叶子结点值置1
return;
}
int mid = ( l + r ) >> 1;
if(p <= mid)
update(p, l, mid, rt << 1);
else
update(p, mid + 1, r, (rt << 1) + 1);
PushUp(rt);
}
int query(int ll,int rr,int l,int r,int rt)
{
if(ll <= l && r <= rr)
{
return num[rt];
}
int mid = (l+r) >> 1;
int res = 0;
if(ll <= mid)
res += query(ll,rr,l,mid,rt << 1);
if(rr > mid)
res += query(ll,rr,mid+1,r,(rt << 1)+1);
return res;
}
int main()
{
int n;
int a[MAXN];
while(~scanf("%d",&n))
{
build(0,n-1,1);
int res = 0;
for(int i = 0; i< n; i++)
{
scanf("%d",&a[i]);
res += query(a[i],n-1,0,n-1,1); //求(a[i],n-1)区间内1的个数
update(a[i],0,n-1,1);
}
int ans = INF;
for(int i = 0; i < n; i++)
{
res = res -a[i] + n - 1 - a[i]; //没次移动,次序对数目变化
ans = ans < res ? ans : res;
}
printf("%d\n",ans);
}
}
hdu 1394 Minimum Inversion Number
标签:
原文地址:http://www.cnblogs.com/yong-hua/p/4657137.html