題目大意:給出乙個數n,
1<=n<=20
,表示共有
2^n個數,給出乙個數
m,表示
m次詢問,每次詢問給出乙個數
q,表示將這
2^n個數分為
2^q組,每組
2^(n-q)
個數,將每組數進行轉置操作,並輸出轉置後所有數中逆序數的對數。
分析:剛開始的時候我在想如何用線段樹在求出逆序數並維護
1<=q<=n
上的每個子區間的逆序數和轉置後的逆序數,未果。檢視題解發現這題做不出來的原因是當初學逆序數的時候只學了樹狀陣列求逆序數,並不了解歸併排序的同時也可以求逆序數。歸併排序求逆序數的求法是,對一段區間
[l,r]
,中值為m,設
1<=i<=m
,m+1<=j<=r
,對於a[i]>a[j]
,那麼左半部分中比
a[i]
大的數一定比
a[j]
大,所以將
a[j]
放置到他的位置的時候恢復位置的逆序數共有
m-i+1
對,根據這個公式我們可以在每次合併的過程中求出每乙個
2^q區間的逆序數,同時對於
a[i],我們可以求出轉置後的逆序數的對數。除此之外,對於歸併排序求逆序數,我們將一段區間轉置以後,這個區間的所有子區間都被轉置,則該區間和他的子區間的逆序數的對數要變成逆序後的逆序數的對數,而對於向上的區間通過模擬可以發現逆序數的對數是不變的,因為左右區間的關係沒有發生改變。因此當對每個長度為
2^q的區間進行轉置操作時只需要將此長度的區間和子區間的逆序數與順序數的數值交換即可。我們用乙個陣列
dp維護每一層的順序數和逆序數。
dp[i][0]
表示第i
層即長度為
2^i區間的逆序數之和,
dp[i][1]
為順序數之和,歸併排序預處理出每一層的逆序數與順序數,每次詢問交換從第
1層至第
q層的順序數與逆序數並重新累加和輸出即可。
**:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include //#include //#include #define ll long long
#define ull unsigned long long
//#define int64 long long
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e2 + 5;
int n, m, a[1 << 21], b[1 << 21];
ll dp[21][2];
void mergesort(int l, int r, int le) else
}i = l, j = m + 1, k = l;
while (i <= m && j <= r) else
}while (i <= m) b[k++] = a[i++];
while (j <= r) b[k++] = a[j++];
for (i = l; i <= r; i++) a[i] = b[i];}
int main()
for (int i = 1; i <= n; i++)
printf("%i64d\n", ans);
}//fclose(stdin);
//fclose(stdout);
return 0;
}
歸併排序 逆序數
對於數列a,將其二分地拆分為b,c 先將b,c分別排序好,再合併b,c即為總的排序,不過在合併的過程中我們可以算出逆序數哦。其原理網上很多,我這裡不再贅述,只給出實現 include include define ll long long using namespace std ll mergeso...
逆序數(歸併排序)
分而治之 分 每次從中間劃分開,直到有序為止,即乙個整數 void merge int s,int left,int right 治重新定義乙個a陣列,儲存排序完的合併陣列,void sort int s,int left,int mid,int right while i mid a k s i ...
求逆序數 逆序數 歸併排序
求排列的逆序數 分治 一 題目描述 總時間限制 1000ms 記憶體限制 65536kb 描述 在internet上的搜尋引擎經常需要對資訊進行比較,比如可以通過某個人對一些事物的排名來估計他 或她 對各種不同資訊的興趣,從而實現個性化的服務。對於不同的排名結果可以用逆序來評價它們之間的差異。考慮1...