1.题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=1394
2.题意描述:
给你一个有0--n-1数字组成的序列,然后进行这样的操作,每次将最前面一个元素放到最后面去会得到一个序列,那么这样就形成了n个序列,那么每个序列都有一个逆序数,找出其中最小的一个输出!
3.思路:
先求出第一个序列的逆序数,然后用很巧妙的办法求下一个序列的逆序数,直到全部求出;
序列 4 5 2 1 3 6 ,此序列的逆序数为7,它等到的下一个序列为 5 2 1 3 6 4
看这个新序列的产生过程,首部删除4,尾部添加4
删除4,必然会使得这个序列的逆序数减少(4-1)个,因为4前面必定有4-1个数小于4
添加4,必然会使得这个序列的逆序数增加(6-4)个,因为4后面必定有6-4个数大于4
由此推出公式,假设移动的数为m,序列的逆序数=上一序列逆序数-(m-1)+(N-m)
由于给出的序列不是顺序的,所以先要离散化出来。
4.感想:
之前一直都不理解什么叫做最小逆序数,搞了N久,突然看到某大牛的博客,恍然大悟,其实先理解什么是最小逆序数在做题这样子比较好。
5.参考代码:
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=5555;
int sum[maxn<<2];
int x[maxn];
void PushUP(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
sum[rt]=0;
if(l==r)
return ;
int m=(l+r)>>1;
build(lson);
build(rson);
PushUP(rt);
}
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 query(int L,int R,int l,int r,int rt){
if(L<=l && r<=R)
return sum[rt];
int m=(l+r)>>1;
int ret=0;
if(L<=m)
ret+=query(L,R,lson);
if(R>m)
ret+=query(L,R,rson);
return ret;
}
int main()
{
int n,i;
while(~scanf("%d",&n))
{
build(0,n-1,1);
int sum=0; ///初始化逆序数
for(i=0;i<n;i++)
{
scanf("%d",&x[i]);
sum+=query(x[i],n-1,0,n-1,1); ///求每个逆序数
update(x[i],0,n-1,1); ///移动
}
int ret=sum;
for(i=0;i<n;i++)
{
sum=sum-x[i]+(n-1-x[i]); ///最小逆序数的巧妙求法
if(sum<ret)
ret=sum;
}
printf("%d\n",ret);
}
return 0;
}