【线段树求逆序数】【HDU1394】Minimum Inversion Number

题目大意:

随机给你全排列中的一个,但不断的把第一个数丢到最后去,重复N次,形成了N个排列,问你这N个排列中逆序数最小为多少


做法:

逆序数裸的是N^2 利用线段树可以降到NlogN


具体方法是插入一个数,查询之前比他大的数有多少个,即query(A[i]+1,N,1,N,1),利用线段树保存区间和即可;


至于不断把一个数丢到最后,可以用O(1)的时间来处理 即  temp=temp-A[i]+(N-A[i]-1);  这个地方很容易思考的 不解释了




完整代码如下:

/*
1.WA 忘记初始化线段树
*/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#define oo 0x13131313
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 5555
int tree[maxn*4];
int N;
int A[maxn];
void input()
{
    for(int i=1;i<=N;i++)
    scanf("%d",&A[i]);
}
int PushUp(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
int updata(int p,int k,int l,int r,int rt)
{
    int m;
    if(l==r) {tree[rt]+=k;return 0;}
    m=(l+r)>>1;
    if(p<=m) updata(p,k,lson);
    else updata(p,k,rson);
    PushUp(rt);
}
int query(int L,int R,int l,int r,int rt)
{
    int temp=0,m;
    if(L<=l&&r<=R) return tree[rt];
    m=(l+r)>>1;
    if(L<=m) temp=temp+query(L,R,lson);
    if(R>m) temp=temp+query(L,R,rson);
    return temp;
}
void solve()
{
    int ans=0x3f3f3f3f;
    int temp=0;
    for(int i=1;i<=N;i++)
    {
        temp=temp+query(A[i]+1,N,1,N,1);
        updata(A[i],1,1,N,1);
    }
    ans=temp;
    for(int i=1;i<=N-1;i++)
    {
        temp=temp-A[i]+(N-A[i]-1);
        if(temp<ans) ans=temp;
    }
    printf("%d\n",ans);
}
int main()
{
	while(cin>>N)
    {
        memset(tree,0,sizeof(tree));
        input();
        solve();
    }
}


posted on 2015-04-08 10:16  DDUPzy  阅读(141)  评论(0编辑  收藏  举报

导航