HDU-1394 Minimum Inversion Number(线段树)

四处翻了翻解题报告,总结需解决两点
1、求初始序列逆序对数和。
这里可以使用线段树或者树状数组。

使用原理就是:利用线段树或者树状数组将 [0 - n-1]排开。
入读一个数时,
1)向后查询。例如读入3 , 那么就看看4 5 6 7 8 ….是否有之前输入过的。
然后累加起来 ,这便是初态的逆序对数。
2)单点更新,找到3这个点,标记一下 方便下次输入查询。

以线段树为例, 初始化线段树各个点sum[rt]=0 。读入一个数a[i]时

向后查询,s+=query(a[i],n-1,0,n-1,1); 前面两位参数是从a[i]向后查询的意思。
用s累加起来

再单点更新,使sum[a[i]]++。 update(a[i],1,0,n-1,1); 意思是从 [0,n-1]区间中 找到a[i] 这个点,进行+1操作

2、交换后的逆序对数, 跟上一个状态有关,s=s-a[i]+(n-1-a[i]); ;也就是这么一个公式带入具体数据推一推。

线段树实现代码如下:

#include <iostream>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 55555
using namespace std;
int sum[maxn<<2];
int cnt=1;
void PushUp(int rt)
{
    sum[rt]=sum[rt*2]+sum[rt*2+1];
}
void build(int l,int r,int rt)
{
    sum[rt]=0;
    if(l==r)
        return ;
    int m=(l+r)/2;
    build(lson);
    build(rson);
    PushUp(rt);
}
void update(int p,int add,int l,int r,int rt)///单点更新, 为了找到p点,用了二分
{
    if(l==r)
    {
        sum[rt]+=add;
        return;
    }
    int m=(l+r)/2;
    if(p<=m) update(p,add,lson);
    else update(p,add,rson);
    PushUp(rt);///不忘回溯
}
int query(int L,int R,int l,int r,int rt)///查询某区间[L,R]的值
{
    if(l==r) return sum[rt];
    int m=(l+r)/2;
    int ret=0;
    if(L<=m) ret+=query(L,R,lson);///找到是【L,R】的子区间
    if(R>m) ret+=query(L,R,rson);
    return ret;
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int  s=0;
        int a[10000];
        build(0,n+1,1);
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            s+= query(a[i],n-1,0,n-1,1);
            update(a[i],1,0,n-1,1);
        }
        int ans=s;
        for(int i=1; i<n; i++)
        {
            s += n - a[i] - a[i] - 1;

            ans=min(ans,s);
        }
        cout<<ans<<endl;
    }

    return 0;
}

个人理解一下使用线段树和树状数组区别,树状数组由于其特性 管理的数只能从 1开始。因此读入时每位i+1(如有错误请指出)

树状数组实现:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=5000;
int c[MAXN+1];
int lowbit(int x)
{
    return x&(-x);
}
int sum(int x)
{
    int res=0;
    while(x>0)
    {
        res+=c[x];
        x-=lowbit(x);
    }
    return res;
}
void add(int x,int v)
{
    while(x<=MAXN)
    {
        c[x]+=v;
        x+=lowbit(x);
    }
}
int a[MAXN];
int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        int counter=0;//计算逆序

        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i]++;//标记
            counter+= sum(n)-sum(a[i]);///读入是0 ,但是我们要的是1  。
            add(a[i],1);
        }
        int ans=counter;//ans保存最终结果
        for(int i=1;i<n;i++)
        {
            counter +=n-2*a[i]+1;
            ans = min(ans,counter);
        }
        printf("%d\n",ans);
    }
}

(如有错误,欢迎支持)

猜你喜欢

转载自blog.csdn.net/qq_37591656/article/details/77053893