树状数组很经典的运用,求逆序数。
题意是要求将数移到最后位置时,最小的逆序数。求一个序列的逆序数可以通过树状数组或者线段树做。树状数组还是很巧妙的,每次读入一个数,将该数存入树状数组C[],查询前面是否有比该数大的数,即为逆序数。
数状数组代码:
#include<stdio.h>
#include<string.h>
int C[5005],n;
int lowbit(int x){
return x&(-x);
}
int query(int x){
int sum=0;
x+=1;
while(x<=n){
sum+=C[x];
x+=lowbit(x);
}
return sum;
}
void update(int pos,int val){
while(pos>0){
C[pos]+=val;
pos-=lowbit(pos);
}
}
int main(){
int sum,x,a[5005];
while(~scanf("%d",&n)){
sum=0;
memset(C,0,sizeof(C));
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
update(a[i]+1,1);
sum+=query(a[i]+1);
}
int min=sum;
// printf("%d\n",sum);
for(int i=0;i<n;i++){
sum=sum+(n-1)-2*a[i];
if(sum<min) min=sum;
}
printf("%d\n",min);
}
return 0;
}
线段树代码:
#include <iostream>
#include<cstdio>
using namespace std;
//#define min(a,b) a>b?b:a
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int maxn=5005;
int sum[maxn<<2];
int a[maxn];
void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
sum[rt]=0;
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return sum[rt];
}
int ret=0;
int m=(l+r)>>1;
if(L<=m) ret+=query(L,R,lson);
if(R>m) ret+=query(L,R,rson);
return ret;
}
void update(int p,int l,int r,int rt){
if(l==r){
sum[rt]++;
return;
}
int m=(l+r)>>1;
if(p<=m) update(p,lson);
else update(p,rson);
pushup(rt);
}
int main()
{
int n;
while(~scanf("%d",&n)){
build(0,n-1,1);
int sum=0;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
sum+=query(a[i],n-1,0,n-1,1);
update(a[i],0,n-1,1);
}
int res=sum;
for(int j=0;j<n;j++){
sum=sum-a[j]+(n-a[j]-1);
res=min(res,sum);
}
printf("%d\n",res);
}
return 0;
}