1. 程式人生 > >清北學堂模擬賽d2t3 逆序對(pair)

清北學堂模擬賽d2t3 逆序對(pair)

return code ios while spa 不用 += oid ret

題目描述
LYK最近在研究逆序對。
這個問題是這樣的。
一開始LYK有一個2^n長度的數組ai。
LYK有Q次操作,每次操作都有一個參數k。表示每連續2^k長度作為一個小組。假設n=4,k=2,則a[1],a[2],a[3],a[4]為一個小組,a[5],a[6],a[7],a[8]為一個小組,a[9],a[10],a[11],a[12]為一個小組,a[13],a[14],a[15],a[16]也為一個小組。
然後LYK對於每個小組都翻轉,也就是說原數組會變成a[4],a[3],a[2],a[1],a[8],a[7],a[6],a[5],a[12],a[11],a[10],a[9],a[16],a[15],a[14],a[13]。之後它想求出這2^n個數的逆序對是多少。
因此你需要輸出對於每次操作,操作完後這2^n個數的逆序對有多少對。
兩個數ai,aj被稱為逆序對當且僅當i<j且ai>aj。

輸入格式(pair.in)
第一行一個數n。
接下來一行2^n個數ai表示一開始的數組。
接下來一行一個數Q,表示操作的次數。
接下來一行Q個數,表示每次操作的參數k。

輸出格式(pair.out)
Q行,表示每次操作後的答案。

輸入樣例
2
2 1 4 3
4
1 2 0 2

輸出樣例
0
6
6
0

樣例解釋
第一次操作,{2,1,4,3}->{1,2,3,4}
第二次操作,{1,2,3,4}->{4,3,2,1}
第三次操作,{4,3,2,1}->{4,3,2,1}
第四次操作,{4,3,2,1}->{1,2,3,4}

對於30%的數據n<=10,Q<=10。
對於50%的數據n<=10,Q<=1000。
對於80%的數據n<=10,Q<=200000。
對於100%的數據n<=17,Q<=200000,1<=ai<=2^n。

分析:比較巧妙的一道題。每次翻轉操作其實就是將一個區間的逆序對個數與順序對個數交換,那麽先用歸並排序求出逆序對的同時求出順序對。接下來可以把翻轉操作進行分解:假設交換翻轉1234

1,2,3,4 ---> 1,2 3,4 ---> 3,4 1,2 --->4,3,2,1.對每一個子區間的逆序對進行分析,可以知道每個元素的貢獻是這個元素在區間長度為1的逆序對數+區間長度為2的逆序對數+區間長度為4的逆序數......

每次修改操作只是更改了長度小於等於2^k的區間的逆序對數,對於長度大於2^k的區間就不用修改了.統計的時候把各個長度的區間答案加起來就是了.

#include <cstdio>
#include 
<cstring> #include <iostream> #include <algorithm> using namespace std; long long n, a[(1 << 17) + 10],q,cnt[(1 << 17) + 10][2],b[(1 << 17) + 10],k; long long ans; void Sort(int l, int r, int dep) { if (l >= r) return; int mid = (l + r) >> 1; Sort(l, mid, dep - 1); Sort(mid + 1, r, dep - 1); int i = l, j = mid + 1, res = 0; while (i <= mid && j <= r) { if (a[i] < a[j]) { cnt[dep][1] += r - j + 1; i++; } else j++; } i = l, j = mid + 1; while (i <= mid && j <= r) { if (a[i] > a[j]) { cnt[dep][0] += mid - i + 1; b[++res] = a[j++]; } else b[++res] = a[i++]; } while (i <= mid) b[++res] = a[i++]; while (j <= r) b[++res] = a[j++]; for (int i = l; i <= r; i++) a[i] = b[i - l + 1]; } int main() { scanf("%lld", &n); for (int i = 1; i <= (1 << n); i++) scanf("%lld", &a[i]); Sort(1, 1 << n, n); scanf("%lld", &q); while (q--) { scanf("%lld", &k); ans = 0; for (int i = 1; i <= n; i++) { if (i <= k) swap(cnt[i][0], cnt[i][1]); ans += cnt[i][0]; } printf("%lld\n", ans); } return 0; }

清北學堂模擬賽d2t3 逆序對(pair)