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.
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