hdu 1394 线段树单点更新

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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值