HDU 1394 Minimm Inversion Number(線分樹:単点更新、逆序数を求める)
3917 ワード
http://acm.hdu.edu.cn/showproblem.php?pid=1394
Minimum Inversion Number
Time Limit:2000/1000 MS(Java/Others) メモリLimit:65536/32768 K(Java/Others)Total Submission(s):14618 Acceepted Submission(s):8942
Problem Description
The inversion number of a given number sequence a 1,a 2,…,an is the number of pairs(a i,a j)that satisfy iaj.
For a given sequence of numbers a 1,a 2,…,a n,if we move the first m=0numbers to the end of the seqence,we will over Sequence.The are totallyn such sequences as:
a 1,a 2,…,an-1,an(where m=0-the initial seqence)
a 2,a 3,…,an,a 1(where m=1)
a 3,a 4,…,an,a 1,a 2(where m=2)
…
an,a 1,a 2,…,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
この問題は暴力でもできるし、巧妙です。しかし、ここでは線分数を使って解く方法を説明します。多くのブログを見てから理解しました。
線分樹を利用すると、元のシーケンスの逆順序数だけが計算され、その後、この逆順数を利用して他のシーケンスの逆順序数が提示される。i番目の数に対して、
彼の逆序数は、線分樹に挿入されたものよりも大きな数の個数に等しい。
最初のシーケンスの逆数を求めた後:
例えば、シーケンス2 0 3 1 4 5の逆序数は3であり、2を最後に置いた後、2より小さい数(2個)の各数の逆序数は1を減らし、2より小さい数(2個)の各数の逆序数は2を減らします。
大きな数(n-a[i]-1個)の逆序数は不変ですが、2の逆序数は2より大きい数の個数(n-a[i]-1)になります。
他のシーケンスの逆序数を提示して最小値を求めることができます。
Minimum Inversion Number
Time Limit:2000/1000 MS(Java/Others) メモリLimit:65536/32768 K(Java/Others)Total Submission(s):14618 Acceepted Submission(s):8942
Problem Description
The inversion number of a given number sequence a 1,a 2,…,an is the number of pairs(a i,a j)that satisfy i
For a given sequence of numbers a 1,a 2,…,a n,if we move the first m=0numbers to the end of the seqence,we will over Sequence.The are totallyn such sequences as:
a 1,a 2,…,an-1,an(where m=0-the initial seqence)
a 2,a 3,…,an,a 1(where m=1)
a 3,a 4,…,an,a 1,a 2(where m=2)
…
an,a 1,a 2,…,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
0からn-1までのランダム・シーケンスをあげて、n種の順序変換後の最小逆順数を求めます。例えば2341逆序数:2の逆序数は1であるので、n=1,3の逆序数は1であるので、n=1+1=2,4の逆序数は1であるので、n=2+1=3,1の逆序数は0であるので、2341の逆序数はn=3である。最初のビット数を後にするたびに、3412の逆順数は4、4123の逆順数は4、1234の逆順数は0であるので、すべての場合、シーケンスの最小逆順数はn=0である。この問題は暴力でもできるし、巧妙です。しかし、ここでは線分数を使って解く方法を説明します。多くのブログを見てから理解しました。
線分樹を利用すると、元のシーケンスの逆順序数だけが計算され、その後、この逆順数を利用して他のシーケンスの逆順序数が提示される。i番目の数に対して、
彼の逆序数は、線分樹に挿入されたものよりも大きな数の個数に等しい。
最初のシーケンスの逆数を求めた後:
例えば、シーケンス2 0 3 1 4 5の逆序数は3であり、2を最後に置いた後、2より小さい数(2個)の各数の逆序数は1を減らし、2より小さい数(2個)の各数の逆序数は2を減らします。
大きな数(n-a[i]-1個)の逆序数は不変ですが、2の逆序数は2より大きい数の個数(n-a[i]-1)になります。
他のシーケンスの逆序数を提示して最小値を求めることができます。
<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int maxn=5010;
int a[maxn];
int n,ans;
struct node{
int left,right,sum;
}t[maxn<<2];
void build(int k,int l,int r)
{
t[k].left=l;t[k].right=r;
t[k].sum=0;
if(l==r) return;
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
}
void update(int k,int val)
{
int l=t[k].left,r=t[k].right;
if(l==val && val==r){
t[k].sum++;
return;
}
int mid=(l+r)>>1;
if(val<=mid) update(k<<1,val);
else update(k<<1|1,val);
t[k].sum=t[k<<1].sum+t[k<<1|1].sum;
}
void query(int k,int l,int r)
{
if(l==t[k].left && r==t[k].right)
{
ans+=t[k].sum;
return;
}
int mid=(t[k].left+t[k].right)>>1;
if(r<=mid) query(k<<1,l,r);
else if(l>mid) query(k<<1|1,l,r);
else{
query(k<<1,l,mid);
query(k<<1|1,mid+1,r);
}
}
int main()
{
while(scanf("%d",&n) != EOF)
{
build(1,0,n);// n-1, a[i]+1, , n
ans=0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
query(1,a[i],n-1);// a[i] 。 : a[i]
update(1,a[i]);// a[i]
}
int temp=ans;
for(int i=0;i<n-2;i++)
{
ans=ans-a[i]+(n-a[i]-1);
temp=min(temp,ans);
}
printf("%d
",temp);
}
return 0;
}
</span>