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

   
   
   
   
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>