hdu 1754 Minimum Inversion Number 线段树 单点更新

题意:输入一串数字,对其进行一次操作:把潜m个数放到末尾,问最少的pair(i,j)(i<j,a[i]>a[j]),输入的数字各不相同,且在[0,n-1]内

做法:先用线段树的手法求出原始序列的pair数,然后在移动时,可以发现一个规律,设每当进行某次操作,位于序列中的第m个数为X,此时序列中的pair数为p,有一个操作移动了m-1个数,最终的pair数为p,在形成的序列中x为与序列的顶部,要计算的那个移动m个数的操作可以认为是把X移到该序列的末尾,此时,比x大的数有n-1-x个,比X小的数有x个,那么把它移到末尾,会增加n-1-x-x个新pair。这样递推,就可以算出各次操作的pair,得出答案

#include<cstdio>
#include<cstring>
#define LMT 5003
#define left l,m,x<<1
#define right m+1,r,x<<1|1
int a[LMT],sum[LMT<<2];
int min(int a,int b)
{
    return a<b?a:b;
}
void update(int key,int l,int r,int x)
{
    if(l==r)
    {
        sum[x]=1;
        return;
    }
    int m=(l+r)>>1;
    if(key<=m)update(key,left);
    if(key>m)update(key,right);
    sum[x]=sum[x<<1]+sum[x<<1|1];
}
int equery(int L,int R,int l,int r,int x)
{
    if(L<=l&&r<=R)return sum[x];
    int m=(l+r)>>1,ret=0;
    if(L<=m)ret+=equery(L,R,left);
    if(R>m)ret+=equery(L,R,right);
    return ret;
}
int main(void)
{
    int n,ret,ans;
    while(~scanf("%d",&n))
    {
        ans=ret=0;
        memset(sum,0,sizeof(sum));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            ret+=equery(a[i]+1,n-1,0,n-1,1);//方便的求出在它之前的比它大的数
            update(a[i],0,n-1,1);
        }
        ans=ret;
        for(int i=0;i<n;i++)
        {
            ret+=n-1-a[i]-a[i];//进行每一次的转至是,总对数的变换方式
            ans=min(ret,ans);
        }
        printf("%d\n",ans);
    }
    return 0;
}



你可能感兴趣的:(hdu 1754 Minimum Inversion Number 线段树 单点更新)