P7629 [COCI2011-2012#1] SORT 題解
阿新 • • 發佈:2021-10-11
sort
這題題意就是給定有 \(n\) 個數的陣列 \(a\),每次將 \(a\) 劃分為多個連續的下降區間,將每個有至少兩個數的區間翻轉。問讓 \(a\) 排好序需要至少多少次翻轉。
\(O(n^2)\) 的做法很顯然,就按題意模擬即可,考慮優化。
假設第一次陣列 \(a\) 劃分成了 \([1,2,\dots,p_1],[p_1+1,p_1+2,\dots,p2],\dots,[p_{s-1}+1,p_{s-2}+1,...,p_s](p_s=n)\),那麼做完第一次輪反轉操作後,每一個區間都是按從小到大排好序的。那麼做第二輪操作時,下降區間就只能跨越第一輪的相鄰區間。比如說:4 2 3 1
4 2 | 3 1
,翻轉之後變成了 2 4 | 1 3
,第二次下降區間只有 4 1
。然後有個顯而易見的結論就是第二次以後的下降區間長度只能為 \(2\)。
那麼問題就轉換為了每次交換相鄰的數,問排好序之後的最小交換次數。然後逆序對做就行了。
程式碼:
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N = 1e5 + 5; int n; LL ans; int a[N], cut[N]; class BIT { public: void upd (int x, int v) { for (; x <= n; x += x & -x) c[x] += v; } LL qry (int x) { LL ret = 0; for (; x; x -= x & -x) ret += c[x]; return ret; } private: LL c[N]; } t ; bool check () { for (int i = 1; i <= n; ++i) if (a[i] != i) return false; return true; } void solve () { int cnt = 0; for (int i = 1; i <= n; ++i) if (a[i] < a[i + 1]) cut[++cnt] = i; for (int i = 1; i <= cnt; ++i) if (cut[i] - cut[i - 1] != 1) { reverse(a + cut[i - 1] + 1, a + cut[i] + 1); ++ans; } for (int i = 1; i <= n; ++i) { ans += i - 1 - t.qry(a[i]); t.upd(a[i], 1); } cout << ans << endl; } int main() { // freopen("sort.in", "r", stdin); // freopen("sort.out", "w", stdout); cin >> n; for (int i = 1; i <= n; ++i) cin >> a[i]; a[n + 1] = n + 1; solve(); cerr << clock() * 1.0 / CLOCKS_PER_SEC << 's' << endl; return 0; }