hdu 1394 Minimum Inversion Number(线段树-单点更新)
2015-08-03 13:59
453 查看
Problem Description
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
Sample Output
就是给出一串数,当依次在将第一个数变为最后一个数的过程中,要你求它的最小逆序数。
先建的是一棵空树,然后每插入一个点之前,统计大于这个数的有多少个,直到所有的数都插入完成,就结果了初始逆序树的统计。
#include <iostream>
#include <stdio.h>
using namespace
std;
#define maxn 5005
int sum[3*maxn];
void PushUp(int root)
{
sum[root]=sum[root<<1]+sum[(root<<1)+1];
}
void Build(int L,int R,int root)
{
sum[root]=0;
if(L==R)
return;
int m=(L+R)>>1;
Build(L,m,root<<1);
Build(m+1,R,(root<<1)+1);
}
void Update(int L,int R,int root,int pos)
{
if(L==R)
{
sum[root]++;
return;
}
int m=(L+R)>>1;
if(pos<=m)
Update(L,m,root<<1,pos);
else
Update(m+1,R,(root<<1)+1,pos);
PushUp(root);
}
int Query(int ql,int qr,int L,int R,int root)
{
if(ql<=L && qr>=R)
{
return sum[root];
}
int m=(L+R)>>1;
int ans=0;
if(ql<=m)
ans+=Query(ql,qr,L,m,root<<1);
if(qr>m)
ans+=Query(ql,qr,m+1,R,(root<<1)+1);
return ans;
}
int val[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
Build(0, n-1,1);
//建空树(初始化)
int sum2=0;
for (int i=0; i<n; i++)
{
scanf("%d",&val[i]);
sum2+=Query(val[i], n-1,0, n-1,
1);
/*
query求出区间[val[i], n - 1]中的点数,因为这个区
间内的点都比val[i]先插入且比seq[i]大(而坐标却比val[i]的坐标小,
故他们之间是逆序数),所以,这个区间内的点的个数就等于val[i]的逆序数,
把这些点的逆序数全加起来,就得到整个序列的逆序数sum。
*/
Update(0, n-1,1,val[i]);
//插入val[i],更新线段内的点数。
}
int ans=sum2;
for (int i=0; i<n; i++)
{
/*
因为序列为[0, n-1],若最前面一个数为x(其坐标最小),序列中比x
小的数为[0, x-1],
共x个,比x大的数为[x+1, n-1],
共n-x-1个,将x移到最后(其坐标最大),比x小的数的逆序数均减1,
x的前面比x大的数有n-x-1个,x的逆序数增加n-x-1。
所以新序列的逆序数为原序列的逆序数加上n-2*x-1。
*/
sum2+=n-2*val[i]-1;
ans=min(ans,sum2);
}
printf("%d\n",ans);
}
return 0;
}
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
就是给出一串数,当依次在将第一个数变为最后一个数的过程中,要你求它的最小逆序数。
先建的是一棵空树,然后每插入一个点之前,统计大于这个数的有多少个,直到所有的数都插入完成,就结果了初始逆序树的统计。
#include <iostream>
#include <stdio.h>
using namespace
std;
#define maxn 5005
int sum[3*maxn];
void PushUp(int root)
{
sum[root]=sum[root<<1]+sum[(root<<1)+1];
}
void Build(int L,int R,int root)
{
sum[root]=0;
if(L==R)
return;
int m=(L+R)>>1;
Build(L,m,root<<1);
Build(m+1,R,(root<<1)+1);
}
void Update(int L,int R,int root,int pos)
{
if(L==R)
{
sum[root]++;
return;
}
int m=(L+R)>>1;
if(pos<=m)
Update(L,m,root<<1,pos);
else
Update(m+1,R,(root<<1)+1,pos);
PushUp(root);
}
int Query(int ql,int qr,int L,int R,int root)
{
if(ql<=L && qr>=R)
{
return sum[root];
}
int m=(L+R)>>1;
int ans=0;
if(ql<=m)
ans+=Query(ql,qr,L,m,root<<1);
if(qr>m)
ans+=Query(ql,qr,m+1,R,(root<<1)+1);
return ans;
}
int val[maxn];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
Build(0, n-1,1);
//建空树(初始化)
int sum2=0;
for (int i=0; i<n; i++)
{
scanf("%d",&val[i]);
sum2+=Query(val[i], n-1,0, n-1,
1);
/*
query求出区间[val[i], n - 1]中的点数,因为这个区
间内的点都比val[i]先插入且比seq[i]大(而坐标却比val[i]的坐标小,
故他们之间是逆序数),所以,这个区间内的点的个数就等于val[i]的逆序数,
把这些点的逆序数全加起来,就得到整个序列的逆序数sum。
*/
Update(0, n-1,1,val[i]);
//插入val[i],更新线段内的点数。
}
int ans=sum2;
for (int i=0; i<n; i++)
{
/*
因为序列为[0, n-1],若最前面一个数为x(其坐标最小),序列中比x
小的数为[0, x-1],
共x个,比x大的数为[x+1, n-1],
共n-x-1个,将x移到最后(其坐标最大),比x小的数的逆序数均减1,
x的前面比x大的数有n-x-1个,x的逆序数增加n-x-1。
所以新序列的逆序数为原序列的逆序数加上n-2*x-1。
*/
sum2+=n-2*val[i]-1;
ans=min(ans,sum2);
}
printf("%d\n",ans);
}
return 0;
}
相关文章推荐
- DOM应用
- sphinx 指定字段内容 高亮
- SecureCRT连接linux,vim颜色显示问题
- Android studio 打包apk如何重命名apk名称
- com.ibatis.common.jdbc.exception.NestedSQLException:
- 一个困扰很久的异常java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
- Mysql 性能监控及调优
- UVA 10765 (割点)
- IOS 定制中间突出UItabBar
- CSS性能优化
- 20150803-STVD中报“ERROR:before starting debug session,please,select a target”错误
- SSH框架快速搭建
- ol,li,ul,dl,dt,dd
- DNS原理介绍和具体搭建DNS
- Android DiskLruCache 源码解析 硬盘缓存的绝佳方案
- linux 进程通信之 信号
- Unity3D内存管理——对象池(Object Pool)
- 二叉树的最低公共父节点
- C语言程序的存储区域
- 通过代码设置radiobutton不同方位图标的两种方法