【HDU 1394】Minimum Inversion Number
阿新 • • 發佈:2018-12-20
題意:輸入一個初始序列,然後每次操作都把序列的第一個數放到最後一個位置,構成一個新的序列,問其中某一個序列的最小逆序數是多少。
思路:首先,普及一下逆序與逆序數的概念,簡單地說就是如果前面的數比後面的數大,就稱為一個逆序。一個排列中逆序的總數稱為這個排列的逆序數。
然後這道題有一個好處就是這n個數是由0~n-1組成的,那麼,這意味著什麼呢?仔細觀察後,我們不難發現
假設0~n-1中的一個數x放到了最後一個位置,那麼這個序列的逆序數就是n-1-x,這是為什麼呢,下面我們接著看
因為在這個序列中,比x小的數有x個(始終記住這n個數是由0~n-1組成的),比x大的數有n-1-x個,那麼,將x放到最後一個位置,也就是說,這個序列中的前n-1個數(去掉最後一個數)中有x個比它小的數,有n-1-x個比它大的數,所以這個序列的逆序數就是n-1-x。
我們假設初始序列的逆序數是ans,那麼把序列中的x放到最後一個位置之後,逆序數就變成了ans - x +(n-1-x),減x是因為原本x是第一個數,後面有x個比它小的數,現在把它放到了最後一個位置,那麼序列中前面有x個對於x來說是有序的,只有那n-1-x個比它大的數才是逆序,所以逆序數要-x。
所以問題就轉化成了求初始序列的逆序數,用線段樹的方法單點更新,區間求和
主要思想就是把每個數放到自己的葉子結點,然後看它前面有多少個比它大的數,最後累加就是序列的逆序數。
My DaiMa:
#include<iostream> #include<stdio.h> #include<math.h> #include<algorithm> using namespace std; #define lson l,mid,num<<1 #define rson mid+1,r,num<<1|1 const int maxn = 5005; int sum[maxn<<2]; void push_up(int num) { sum[num] = sum[num<<1] + sum[num<<1|1]; } /*建樹*/ void TreeBuild(int l,int r,int num) { sum[num] = 0; if(l == r) return; int mid = (l+r) >> 1; TreeBuild(lson); TreeBuild(rson); push_up(num); } /*每插入一個數,更新*/ void update(int x,int l,int r,int num) { if(l == r) { sum[num] ++; return; } int mid = (l+r) >> 1; if(x > mid) update(x,rson); else update(x,lson); push_up(num); } /*查詢,判斷前面有多少個比它大的數*/ int query(int x,int y,int l,int r,int num) { if(x <= l && y >= r) return sum[num]; int ans = 0; int mid = (l+r) >> 1; if(x <= mid) ans += query(x,y,lson); if(y > mid) ans +=query(x,y,rson); return ans; } int main() { int n,a[5005]; while(cin >> n) { TreeBuild(0,n-1,1); int ans = 0; for(int i = 0; i < n; i++) { scanf("%d",&a[i]); ans += query(a[i],n-1,0,n-1,1);//先查詢 update(a[i],0,n-1,1);//後更新 } int minn = ans; for(int i = 0; i < n; i++) { ans = ans - a[i] + (n - 1 - a[i]); minn = min(minn,ans); } cout << minn << endl; } }