HDU 1394 Minimum Inversion Number(线段树:单点更新,区间求和或树状数组:求逆序对)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 1394 Minimum Inversion Number(线段树:单点更新,区间求和或树状数组:求逆序对)相关的知识,希望对你有一定的参考价值。


 

 

The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj.

For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following:

a1, a2, ..., an-1, an (where m = 0 - the initial seqence)
a2, a3, ..., an, a1 (where m = 1)
a3, a4, ..., an, a1, a2 (where m = 2)
...
an, a1, a2, ..., an-1 (where m = n-1)

You are asked to write a program to find the minimum inversion number out of the above sequences.

 

Input

The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1.

 

Output

For each case, output the minimum inversion number on a single line.

 

Sample Input


 


10 1 3 6 9 0 8 5 7 4 2

 

Sample Output


 


16

 

Author

CHEN, Gaoli

 

Source

​​ZOJ Monthly, January 2003 ​​

 

Recommend

Ignatius.L   |   We have carefully selected several similar problems for you:  ​​1698​​ ​​1540​​ ​​1255​​ ​​2795​​ ​​1828​​ 

 

 

 

 

 

 

 

 

题意:给一个数字n,然后给出0~n-1的排列,求出逆序对数,然后,把a[1]放在a[n]后面,在求逆序对数,以此类推,求最小的逆序对数。

分析1(树状数组做法):

首先树状数组求逆序对很好求,注意要加1,因为树状数组不能维护从0开始,关键是本题的一个规律。

1~n的排列)的排列第一个位置和最后一个位置是很特殊的:

    与第一个位置的匹配的逆序数就是比他的小的数的个数:a[1]-1

         与最后一个位置的匹配的逆序数就是比他的大的数的个数:n-a[1]

所以,如果将第一个移动到最后一个且排列前的逆序数为ans,则移动后的逆序数为ans-(a[1]-1)+n-a[1]

 

#include<stdio.h>
#include<string>
#include<string.h>
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN=200000+5;//最大元素个数
int n;//元素个数
ll c[MAXN],a[MAXN];//c[i]==A[i]+A[i-1]+...+A[i-lowbit(i)+1]
//返回i的二进制最右边1的值
int lowbit(int i)

return i&(-i);

//返回A[1]+...A[i]的和
ll sum(int x)
ll sum = 0;
while(x)
sum += c[x];
x -= lowbit(x);

return sum;

//令A[i] += val
void add(int x, ll val)

while(x <= n)
c[x] += val;
x += lowbit(x);


int main()

while(scanf("%d",&n)!=-1)


memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)

scanf("%lld",&a[i]);
a[i]++;

int ans=0;
for(int i=n;i>0;i--)

ans+=sum(a[i]-1);
add(a[i],1);

int minn=ans;
for(int i=1;i<n;i++)


ans+=n-a[i]-(a[i]-1);
minn=min(minn,ans);

cout<<minn<<endl;

return 0;

分析2(线段树做法):

线段树比区间更强大了,正序倒序均可

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define N 200010
#define ll long long
using namespace std;
ll a[N];
int n,m;
struct node //线段树

int l,r;
ll dat;
tree[N<<2];
void PushUp(int x) //线段树维护结点信息

tree[x].dat=tree[x<<1].dat+tree[x<<1|1].dat;

void build(int x,int l,int r) //线段树建树

tree[x].l=l;
tree[x].r=r;
if (l==r) //叶子节点

tree[x].dat=0;
return;

int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
PushUp(x);

ll query(int x,int l,int r) //线段树区间查询

if (l<=tree[x].l&&r>=tree[x].r)
return abs(tree[x].dat); //找到

int mid=(tree[x].l+tree[x].r)>>1;

ll ans=0;
if (l<=mid) ans+=query(x<<1,l,r);
if (r>mid) ans+=query(x<<1|1,l,r);
return ans;

void update(int x,int y,ll k) //线段树单点修改

if (tree[x].l==tree[x].r)

tree[x].dat += k;
return ;

int mid=(tree[x].l+tree[x].r)>>1;
if (y<=mid) update(x<<1,y,k);
else update(x<<1|1,y,k);
PushUp(x);


int main()


while(scanf("%d",&n)!=-1)

memset(tree,0,sizeof(node));
for(int i=1;i<=n;i++)

scanf("%lld",&a[i]);
a[i]++;

build(1,1,n);
int ans=0;
for(int i=n;i>0;i--)

ans+=query(1,1,a[i]-1);
update(1,a[i],1);

int minn=ans;
for(int i=1;i<n;i++)


ans+=n-a[i]-(a[i]-1);
minn=min(minn,ans);

cout<<minn<<endl;

return 0;

 

以上是关于HDU 1394 Minimum Inversion Number(线段树:单点更新,区间求和或树状数组:求逆序对)的主要内容,如果未能解决你的问题,请参考以下文章

hdu1394 [Minimum Inversion Number]

HDU 1394 Minimum Inversion Number

HDU1394 Minimum Inversion Number

[HDU1394]Minimum Inversion Number

hdu 1394 Minimum Inversion Number - 树状数组

[hdu1394]Minimum Inversion Number(树状数组)