hdu1394线段树

求一个排列的最小逆序数,只能够通过将第一个数字不断后移的方式更改顺序。由于题目给出的数字是0-n-1所以每次将首个数字向后移时都满足(假设逆序数为ans首个数字为a[e])ans加n-1-a[e]个逆序数再减去a[e]个逆序数(数学问题,那么问题就很简单了,先求出初始逆序数,再用公式不断计算,得出最小值。关键在于怎么求初始逆序数。用线段树,每次输入数字,将该数字叶子节点数更新1,并求输入这个数字时前面已有的叶子节点(即首顺序中这个数字前的数字)比该数字大的数不断叠加即可得到逆序数。

#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
int n,m;
char c[10];
const int maxn=50005;
int node[maxn<<2];
int t[maxn];
void pushup(int nd)
{
    node[nd]=node[nd<<1]+node[nd<<1|1];
}
void build(int l,int r,int nd)
{
    if(l==r)
    {node[nd]=0;
    return;}
    int mid=(l+r)>>1;
    build(l,mid,nd<<1);
    build(mid+1,r,nd<<1|1);
    pushup(nd);
}
void update(int pos,int l,int r,int nd)
{
    if(l==r)
    {
        node[nd]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(mid<pos)update(pos,mid+1,r,nd<<1|1);
    else update(pos,l,mid,nd<<1);
        pushup(nd);
}
int query(int s,int e,int l,int r,int nd)
{
    if(s<=l&&e>=r)
    {
        return node[nd];
    }
    int ans=0;
    int mid=(l+r)>>1;
    if(s<=mid)ans+=query(s,e,l,mid,nd<<1);
    if(e>mid)ans+=query(s,e,mid+1,r,nd<<1|1);
    return ans;
}
int main()
{
        while(cin>>n)
        {  build(0,n-1,1);
        int ans=0;
            for(int i=0;i<n;i++)
            {cin>>t[i];
            ans+=query(t[i]+1,n-1,0,n-1,1);
            update(t[i],0,n-1,1);}
            int minn=ans;
            for(int i=0;i<n-1;i++)
                {
                ans+=n-1-t[i]*2;
                minn=min(minn,ans);
                }
            cout<<minn<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43331783/article/details/88046479
今日推荐