HDU-2612
阿新 • • 發佈:2020-07-17
運用了分治的思想,將一個數組分成幾乎相等的兩份,分別將兩段中第一個最小的數拿出來放在一個臨時陣列中,直到全部取完。因為是遞迴的,所以每一段的數列都是排序好的。
void merge_sort(ll *A,ll *B,int x,int y) { if(y-x<=1)return; int m=x+(y-x>>1); int p=x,q=m,i=x; merge_sort(A,B,x,m); merge_sort(A,B,m,y); while(p<m or q<y) { if(q>=y or (p<m and A[p]<=A[q]))B[i++]=A[p++];else B[i++]=A[q++]; } for(int i=x;i<y;i++)A[i]=B[i]; }
(沒驗證,有錯誤請指出)
這段程式碼中左端為閉,右端為開。因此呼叫時要將右端點+1
O(nlogn)
它有一個神奇的應用:求逆序對(的和)!因為我們發現每次遞迴時左邊的還沒有入列的數都是大於右邊的數的,所以我們只要在else句的後面加上ans+=m-p就好辣!
#include<iostream> #include<cstdio> using namespace std; typedef long long ll; template<typename T> inline T read() { int w=0;T x=0;char c=getchar(); while(!isdigit(c))w|=c=='-',c=getchar(); while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar(); return w?-x:x; } const int maxn=5e5+10; int n; ll A[maxn],B[maxn],ans; void merge_sort(ll *A,ll *B,int x,int y) {if(y-x<=1)return; int mid=x+(y-x)/2; int p=x,q=mid,i=x; merge_sort(A,B,x,mid); merge_sort(A,B,mid,y); while(p<mid or q<y) { if(q>=y or (p<mid and A[p]<=A[q]))B[i++]=A[p++]; else B[i++]=A[q++],ans+=mid-p; } for(int i=x;i<y;i++)A[i]=B[i]; } int main() { n=read<int>(); for(int i=1;i<=n;i++)A[i]=read<ll>(); merge_sort(A,B,1,n+1); printf("%lld\n",ans); return 0; }