hdu 1394
题意:
有一个序列,a1,a2,.......,an,现在要使该序列经如下变换:
a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)
求在该变换中逆序对数总和最小值。
解题思路:
我们可以先用线段树来求最初的序列 a1,a2,a3,……,an每个数的逆序对数....首先我们可以将a1到an依次插入到线段树中,按他们的数值插入到线段树对应的位置,并使对应的线段树的结点变为1,在向上更新,然后只要判断在 1~当前插入数-1区间内有多少已经插入的,则可以得到,有多少小于当前插入的数已经插入,即可得该数的逆序数。
通过上述操作,我们可以得到每个数的逆序数,和当前序列的逆序总数,所以当我们推到下一个序列时,且此时ai为开头,所以将ai放入最后时,新的逆序总数为 原逆序总数 - ai + n -1 - ai
注意:
注意序列下标与线段树下标对应
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXN0 5005
#define l1(x) (x)<<1
#define r1(x) (x)>>1
struct TRnode{
int L,R,sum;
};
TRnode TR[MAXN0<<2];
int nx[MAXN0];
int a[MAXN0];
void buildTR(int L,int R,int k){
TR[k].L = L;
TR[k].R = R;
TR[k].sum = 0;
if(L==R){
return;
}
int mid = r1(L+R);
int k1,k11;
k1 = l1(k);
k11 = k1 + 1;
buildTR(L,mid,k1);
buildTR(mid+1,R,k11);
}
int query(int L,int R,int k){
if(R<L)return 0;
if(TR[k].L==L&&TR[k].R==R){
return TR[k].sum;
}
int mid = r1(TR[k].L+TR[k].R),k1,k11;
k1 = l1(k);
k11 = k1 + 1 ;
if(mid>=R){
return query(L,R,k1);
}
else if(mid<L){
return query(L,R,k11);
}
else {
return query(L,mid,k1)+query(mid+1,R,k11);
}
}
void pu(int k,int k1,int k11){
TR[k].sum = TR[k1].sum + TR[k11].sum;
}
void update(int num,int k){
if(TR[k].L==TR[k].R){
TR[k].sum = 1;
return;
}
int mid = r1(TR[k].L+TR[k].R);
int k1,k11;
k1 = l1(k);
k11 = k1 + 1;
if(mid>=num){
update(num,k1);
}
else {
update(num,k11);
}
pu(k,k1,k11);
}
int main(){
int n,num,ans,sumnx;
while(scanf("%d",&n)!=EOF){
buildTR(1,n,1);
sumnx = 0;
for(int i=0;i<n;++i){
scanf("%d",&num);
a[i] = num;
nx[i] = num - query(1,num,1);
sumnx+=nx[i];
update(num+1,1);
}
int ans = sumnx;
int up = n - 1;
int tmp,tmp1 = 0,tmp2;
tmp2 = n-1;
for(int i=0;i<up;++i){
sumnx = sumnx - a[i]+up - a[i];
if(sumnx<ans)
ans = sumnx;
//--tmp;
//tmp2 += (up-i);
}
printf("%d\n",ans);
}
return 0;
}