当前位置: 代码迷 >> 综合 >> HDU-1394 Minimum Inversion Number(线段树)
  详细解决方案

HDU-1394 Minimum Inversion Number(线段树)

热度:93   发布时间:2023-11-19 22:51:09.0

四处翻了翻解题报告,总结需解决两点
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);}
}

(如有错误,欢迎支持)

  相关解决方案