10 1 3 6 9 0 8 5 7 4 2
16
题意:
求最小逆序数。进行n次,每次把第一个数放到最后面。
题解:
求解一组逆序数,之后ans=ans+n-a[i]*2+1;求最小值。
关于逆序数思路(大神神讲解):http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html
. 离散之后,怎么使用离散后的结果数组来进行树状数组操作,计算出逆序数?
如果数据不是很大, 可以一个个插入到树状数组中,
每插入一个数, 统计比他小的数的个数,
对应的逆序为 i- getsum( aa[i] ),
其中 i 为当前已经插入的数的个数,
getsum( aa[i] )为比 aa[i] 小的数的个数,
i- sum( aa[i] ) 即比 aa[i] 大的个数, 即逆序的个数
但如果数据比较大,就必须采用离散化方法
假设输入的数组是9 1 0 5 4, 离散后的结果aa[] = {5,2,1,4,3};
在离散结果中间结果的基础上,那么其计算逆序数的过程是这么一个过程。
1,输入5, 调用upDate(5, 1),把第5位设置为1
1 2 3 4 5
0 0 0 0 1
计算1-5上比5小的数字存在么? 这里用到了树状数组的getSum(5) = 1操作,
现在用输入的下标1 - getSum(5) = 0 就可以得到对于5的逆序数为0。
2. 输入2, 调用upDate(2, 1),把第2位设置为1
1 2 3 4 5
0 1 0 0 1
计算1-2上比2小的数字存在么? 这里用到了树状数组的getSum(2) = 1操作,
现在用输入的下标2 - getSum(2) = 1 就可以得到对于2的逆序数为1。
3. 输入1, 调用upDate(1, 1),把第1位设置为1
1 2 3 4 5
1 1 0 0 1
计算1-1上比1小的数字存在么? 这里用到了树状数组的getSum(1) = 1操作,
现在用输入的下标 3 - getSum(1) = 2 就可以得到对于1的逆序数为2。
4. 输入4, 调用upDate(4, 1),把第5位设置为1
1 2 3 4 5
1 1 0 1 1
计算1-4上比4小的数字存在么? 这里用到了树状数组的getSum(4) = 3操作,
现在用输入的下标4 - getSum(4) = 1 就可以得到对于4的逆序数为1。
5. 输入3, 调用upDate(3, 1),把第3位设置为1
1 2 3 4 5
1 1 1 1 1
计算1-3上比3小的数字存在么? 这里用到了树状数组的getSum(3) = 3操作,
现在用输入的下标5 - getSum(3) = 2 就可以得到对于3的逆序数为2。
6. 0+1+2+1+2 = 6 这就是最后的逆序数
分析一下时间复杂度,首先用到快速排序,时间复杂度为O(NlogN),
CODE:
线段树:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#define N 5010
#define Mod 10000007
#define lson l,mid,idx<<1
#define rson mid+1,r,idx<<1|1
#define lc idx<<1
#define rc idx<<1|1
const double EPS = 1e-11;
const double PI = acos ( -1.0 );
const double E = 2.718281828;
typedef long long ll;
const int INF = 1000010;
using namespace std;
int tree[N<<2],n,a[N];
void build(int l,int r,int idx)
{
tree[idx]=0;
if(l==r)
return;
int mid=(l+r)>>1;
build(lson);
build(rson);
}
void update(int l,int r,int idx,int x)
{
if(l==r)
{
tree[idx]++;
return;
}
int mid=(l+r)>>1;
if(x<=mid)
update(lson,x);
else
update(rson,x);
tree[idx]=tree[rc]+tree[lc];
}
int query(int l,int r,int idx,int x,int y)
{
if(l>=x&&y>=r)
{
return tree[idx];
}
int ans=0;
int mid=(l+r)>>1;
if(x<=mid)
ans+=query(lson,x,y);
if(y>mid)
ans+=query(rson,x,y);
return ans;
}
int main()
{
while(cin>>n)
{
build(1,n,1);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
a[i]++;
}
int ans=0;
for(int i=1; i<=n; i++)
{
ans+=query(1,n,1,a[i]+1,n);
update(1,n,1,a[i]);
}
int Min=ans;
for(int i=1;i<=n;i++)
{
ans+=n-2*a[i]+1;
Min=min(Min,ans);
}
cout<<Min<<endl;
}
return 0;
}
树状数组:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#define N 100010
#define Mod 10000007
#define lson l,mid,idx<<1
#define rson mid+1,r,idx<<1|1
#define lc idx<<1
#define rc idx<<1|1
const double EPS = 1e-11;
const double PI = acos ( -1.0 );
const double E = 2.718281828;
typedef long long ll;
const int INF = 1000010;
using namespace std;
int sum[5005];
int bit[5050],a[5050];
int n;
int getsum(int i)
{
int s=0;
while(i>0)
{
s+=bit[i];
i-=i&-i;
}
return s;
}
void add(int i,int x)//更新
{
while(i<=n)
{
bit[i]+=x;
i+=i&-i;
}
}
int main()
{
while(cin>>n)
{
memset(bit,0,sizeof bit);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
a[i]++;
}
int ans=0;
for(int i=1; i<=n; i++)
{
add(a[i],1);
sum[i]=i-getsum(a[i]);
ans+=sum[i];
}
int x=0;
int Min=ans;
for(int i=1; i<=n; i++)
{
ans+=n-a[i]*2+1;
Min=min(Min,ans);
}
cout<<Min<<endl;
}
return 0;
}
Hdu 1394 Minimum Inversion Number(线段树或树状数组)
原文:http://blog.csdn.net/acm_baihuzi/article/details/43085575