1. 程式人生 > >【bzoj4240】 有趣的家庭菜園 樹狀陣列

【bzoj4240】 有趣的家庭菜園 樹狀陣列

這一題最終要構造的序列顯然是一個單峰序列

首先有一個結論:一個序列通過交換相鄰的元素,進行排序,最少的交換次數為該序列的逆序對個數

(該結論很久之前打表意外發現的,沒想到用上了。。。。。)

考慮如何構造這個單峰序列

首先最大的數肯定是該序列的峰,餘下的元素我們從大到小列舉,判斷將其加入到當前序列的左邊還是右邊。

將某個數x移動到峰的兩側,所需要的步數為min(左側>x的數的個數,右側>x的數的個數)。

感性理解的證明就是:若要移動到峰的左側/右側,在這個時候,比它大的數字已經移動到了它的右側/左側,總共會有(>x的數的的個數)個數,跨過這個數。

然後一個樹狀陣列判斷下就沒了。

 1
#include<bits/stdc++.h> 2 #define L long long 3 #define M 300005 4 #define lowbit(x) ((x)&(-x)) 5 using namespace std; 6 7 int n,a[M]={0}; 8 void add(int x,int k){for(;x<=n;x+=lowbit(x)) a[x]+=k;} 9 int query(int x){int k=0;for(;x;x-=lowbit(x)) k+=a[x]; return k;} 10 struct node{
11 int id,x; node(){id=x=0;} 12 node(int iid,int xx){id=iid; x=xx;} 13 friend bool operator <(node a,node b){return a.x>b.x;} 14 }b[M]; 15 16 int main(){ 17 scanf("%d",&n); 18 for(int i=1;i<=n;i++){ 19 int x; scanf("%d",&x); 20 b[i]=node(i,x);
21 } 22 sort(b+1,b+n+1); 23 L ans=0; 24 for(int i=1,j;i<=n;){ 25 for(j=i;b[i].x==b[j].x;j++){ 26 int pos=query(b[j].id); 27 ans+=min(pos,i-1-pos); 28 } 29 for(;i<j;i++) add(b[i].id,1); 30 } 31 cout<<ans<<endl; 32 }