您的位置:首页 > 其它

HDU 1394 Minimum Inversion Number( 归并排序 & 线段树 )

2016-12-12 23:48 260 查看
漫漫长夜无心入眠,来一发

原题链接:Here!

分析:

    一个由0..n-1组成的序列,每次可以把队首的元素移到队尾,求形成的n个序列中最小逆序对数目

    首先求出来序列没有进行全排列时的逆序数为cnt 

    假设现在把首数字放到最后,那么逆序数的变化是什么样的?

    将第一个数t[0]放到t[n-1]后,t[0]后无任何数字所以逆序数增加了0 但是拿走t[0]后,原本t[0]产生的逆序数将会减少

    例如当t[0]=3时,会产生逆序数0 1 2共三个,拿走t[0]后逆序数将-t[0]个。但会增加n-(t[0]+1)因为前面还有n-t[0]-1个数

    会因为t[0]的后移使逆序数增加...... 

    所以每次后移逆序数的变化都为 cnt+n-2*t[0]-1 

    我说的真垃圾,感觉乱七八糟

代码:

    一归并排序 
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int maxn = 5000+10;
int cnt;

void marge_sort(int *A,int x,int y,int *T){ // O(nlogn)
if(y-x>1){
int m=x+(y-x)/2;
int p=x,q=m,i=x;
marge_sort(A,x,m,T); marge_sort(A,m,y,T);
while( p<m || q<y ){
if( q>=y || (p<m && A[p]<=A[q])) T[i++]=A[p++]; // 处理左侧
else T[i++]=A[q++] , cnt += m-p;
}
for(int i=x;i<y;i++) A[i]=T[i];
}
}
int main(){
int n;
int A[maxn],T[maxn],tmp[maxn];
while(scanf("%d",&n)!=EOF){
cnt=0;
for(int i=0;i<n;i++){
scanf("%d",&A[i]); tmp[i]=A[i];
}
marge_sort(A,0,n,T);
// printf("count=%d\n",cnt);
int ans=cnt;
for(int i=0;i<n;i++){
cnt = cnt+n-2*tmp[i]-1;
if(cnt<ans) ans=cnt;
}
printf("%d\n",ans);
}
return 0;
}

    线段树版
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define N 51234
#define root 1 , n , 1
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1

long long sum[N<<2],add1[N<<2],num
;

void pushUp(int rt){
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}

void pushDown1(int l,int r,int rt){
if(add1[rt]){
int m = l+r>>1;
add1[rt<<1] = add1[rt<<1|1] = add1[rt];
sum[rt<<1] = (m-l+1)*add1[rt];
sum[rt<<1|1] = (r-m)*add1[rt];
add1[rt] = 0;
}
}

void update1(int l,int r,int rt,int ql,int qr,int val){
if(l>qr||ql>r)return;
if(l>=ql&&r<=qr){
sum[rt] = (r-l+1)*val;
add1[rt] = val;
return;
}
pushDown1(l,r,rt);
int m = l+r>>1;
if(ql<=m) update1(lson,ql,qr,val);
if(qr>m) update1(rson,ql,qr,val);
pushUp(rt);
}

long long query(int l,int r,int rt,int ql,int qr){
if(l>qr||r<ql)
return 0;
if(l>=ql&&r<=qr)
return sum[rt];
pushDown1(l,r,rt);
int m = l+r>>1;
return query(l,m,rt<<1,ql,qr)+query(m+1,r,rt<<1|1,ql,qr);
}

int main(){
int i,j,n,res,ans;

while(scanf("%d",&n)!=EOF&&n){
memset(sum,0,sizeof(sum));
memset(add1,0,sizeof(add1));
res = 0;

for(i=1;i<=n;i++){
scanf("%d",&num[i]);
num[i]+=1;
res += query(root,num[i],n);
update1(root,num[i],num[i],1);
}
ans = res;
//num[i]从头移到尾,逆序数加上num[i]后面小于num[i]的数n-num[i];
//再减去num[i]后面大于num[i]的数num[i]-1;
for(i=1;i<n;i++){
res = res+n-num[i]-num[i]+1;
ans = min(ans,res);
}
printf("%d\n",ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: