http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意描述:给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找出其中最小的一个输出!
解析:
求出a1, a2, ..., an-1, an的逆序数之后,就可以递推求出其他序列的逆序数。 假设要把a1移动到an之后,那么我们把这个过程拆分成两步:
1. 把a1去除掉。通过观察可以发现,(a1-1)是0~n-1中比a1小的数字的个数,由于a1在序列的第一个所以a1之后共有(a1-1)个比a1小,所以形成了(a1-1)对逆序数,当去除掉a1时,原序列的逆序数总数也就减少了(a1-1)个逆序数。
2. 把a1加到an之后。0~n-1中,比a1大的数共有(n-a1)个数,由于a1现在在最后一个,也就是它前面共有(n-a1)个数比它大,即增加了(n-a1)对逆序数。
综合1,2两步, 设原序列逆序数为sum, 当把原序列第一个移动到最后位置时,逆序数变为:sum = sum-(ai-1)+(n-ai);
3.由于此题范围是0到n-1;所以sum = sum -(ai) + (n - 1 - ai);
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 5050;
struct node
{
int l,r;
int num; //表示该区间已经出现节点的个数
}tree[N*4];
void bulid(int rt ,int l,int r)
{
tree[rt].l=l;
tree[rt].r=r;
tree[rt].num=0;
if(l==r)
return;
int mid = (l+r)/2;
bulid(2*rt,l,mid);//创建左子树
bulid(2*rt+1,mid+1,r);
}
int search(int rt, int l,int r)
{
if(l>r)
return 0;
if(l==tree[rt].l&&r == tree[rt].r)
return tree[rt].num;
int mid = (tree[rt].l+tree[rt].r)/2;
if(r<=mid)
return search(2*rt,l,r);
else if(l>mid)
return search(2*rt+1,l,r);
else
return search(2*rt,l,mid)+search(2*rt+1,mid+1,r);
}
void update(int rt, int x)
{
tree[rt].num++;
if(tree[rt].l==tree[rt].r)
return;
int mid = (tree[rt].l+tree[rt].r)/2;
if(x<=mid)
update(2*rt,x);
else update(2*rt+1,x);
}
/*void print(int rt)
{
printf("%d %d %d\n",tree[rt].l, tree[rt].r , tree[rt].num);
if(tree[rt].l==tree[rt].r)
return;
print(2*rt);
print(2*rt+1);
}*/
int main ()
{
int a[N];
int n;
int i, min;
while(scanf("%d",&n)!=EOF)
{
bulid(1,0,n-1);
int sum = 0;
for(i=0;i<n;i++)
scanf("%d",&a[i]);
for(i=0;i<n;i++)
{
sum+=search(1,a[i]+1,n-1);//查找在a[i]之前出现且比a[i]大的数
// cout << sum << endl;
update(1,a[i]);
}
min =sum;
for(i=0;i<n-1;i++)
{
sum += (n-a[i]-1)-a[i];
if(min>sum)
min =sum;
}
printf("%d\n",min);
}
return 0;
}