Hdu 1394 Minimum Inversion Number

来源:互联网 发布:java 流程图框架 编辑:程序博客网 时间:2024/06/06 19:03

题意:输入n,下面给出n个数,分别是0-n,顺序不定,可以通过对序列进行移动,每次移动把最前面的数移到后面,问通过移动形成的序列,使得得到的逆序数最少

思路:这题的特殊性方便了我们去寻找最小值。现在假设我们知道了原序列的逆序数是first,那么每次移动后怎么算出新的逆序数呢?因为每次都只是移动头元素,假设头元素为x,那么可以知道由x产生的逆序对的个数为x,因为有x个数小于它(0,1,2……x-1),如果将它放到了末尾,那么这x个逆序对将会消失。同样地,整个序列中有(n-1-x)个元素大于x,那么x移到了末尾,将产生(n-1-x)个新的逆序对(这些逆序对分别为(x+1,x),(x+2,x),(x+3,x)……(n-1,x))。因此可以递推地解决这个问题。如果知道了当前序列逆序数为sum,那么移动头元素后的逆序数将会是sum-x+(n-1-x)

那么接下来的问题就是求原序列的逆序数,得到它就可以知道所有其他序列的逆序数。用线段数解决

看列子:(某大神的解释)

先以区间[0,9]为根节点建立val都为0的线段树,
再看看怎样求下面序列的逆序数:
1 3 6 9 0 8 5 7 4 2
在线段树中插入1, 插入之前先询问区间[1,9]已插入的节点数(如果存在,必与1构成逆序) v1=0
在线段树中插入3, 插入之前先询问区间[3,9]已插入的节点数(如果存在,必与3构成逆序) v2=0
在线段树中插入6, 插入之前先询问区间[6,9]已插入的节点数(如果存在,必与6构成逆序) v3=0
在线段树中插入9, 插入之前先询问区间[9,9]已插入的节点数(如果存在,必与9构成逆序) v4=0
在线段树中插入0, 插入之前先询问区间[0,9]已插入的节点数(如果存在,必与0构成逆序) v5=4
在线段树中插入8, 插入之前先询问区间[8,9]已插入的节点数(如果存在,必与8构成逆序) v6=1
在线段树中插入5, 插入之前先询问区间[5,9]已插入的节点数(如果存在,必与5构成逆序) v7=3
在线段树中插入7, 插入之前先询问区间[7,9]已插入的节点数(如果存在,必与7构成逆序) v8=2
在线段树中插入4, 插入之前先询问区间[4,9]已插入的节点数(如果存在,必与4构成逆序) v9=5
在线段树中插入2, 插入之前先询问区间[2,9]已插入的节点数(如果存在,必与2构成逆序) v10=7
累加v1+……+v10 =22,这就是1 3 6 9 0 8 5 7 4 2的逆序数了.

其实就是统计一个区间内已经插入了多少个元素,也可以说成是区间求和(一个区间能达到的最大和就是该区间长度,现在的和就是已经插入在内的元素个数)

代码:

#include <iostream>#include <stdio.h>#include <cstring>#include <cmath>#include <vector>#include <algorithm>#include <map>#include <queue>#include <stack>#define lson l,mid,num<<1#define rson mid+1,r,num<<1|1using namespace std;const int M = 5005;int sum[M<<2];void PushUp(int num){    sum[num]=sum[num<<1]+sum[num<<1|1];}void build(int l,int r,int num){    if(l==r)return;    int mid=(l+r)>>1;    build(lson);    build(rson);}void update(int p,int l,int r,int num){    if(l==r)    {        sum[num]++;        return;    }    int mid=(l+r)>>1;    if(p<=mid)update(p,lson);    if(p>mid)update(p,rson);    PushUp(num);}int query(int L,int R,int l,int r,int num){    if(L<=l && r<=R)    return sum[num];    int mid=(l+r)>>1;    int ans=0;    if(L<=mid)ans+=query(L,R,lson);    if(R>mid)ans+=query(L,R,rson);    return ans;}int main(){    int n,x[M];    while(scanf("%d",&n)==1)    {        memset(sum,0,sizeof(sum));        int ans=0;        build(0,n-1,1);        for(int i=1; i<=n; i++)        {            scanf("%d",&x[i]);            ans+=query(x[i],n-1,0,n-1,1);            update(x[i],0,n-1,1);        }        int mmin=ans;        for(int i=1;i<=n;i++)        {            ans=ans-x[i]+(n-x[i]-1);            mmin=min(mmin,ans);        }        printf("%d\n",mmin);    }    return 0;}


原创粉丝点击