題解-P3149 【排序】
阿新 • • 發佈:2020-10-07
標籤:樹狀陣列(求逆序對)
有點套路
題意
在一段序列中,每次選定一個數,將這個序列中小於這個數的所有數排序,並順序插入它們原來的位置上。(本次操作會對下一次影響)
問每次操作完了之後(和沒操作時)的逆序對個數。
這道題目的逆序對意思有點奇怪,剛開始的逆序對包括相等的兩個數。
思路
以下操作 \(n\) 指對第 \(n\) 個數進行上述操作
先離散化。
對於一個較大數的操作,是可以從較小的數的操作推導過來的,我們可以這樣想:
假設現在已經對於數操作 \(n\) 完了,那麼對於操作 \(n+1\) ,它相對於上一個操作減少的逆序對只是在每個數 \(n+1\)
可以這樣理解:
-
第一種逆序對,是兩個小於 \(n+1\) 的陣列成,顯然已經沒有了,因為操作n已經去除了。
-
第二種逆序對,是一個數 \(n+1\) 和小於 \(n+1\) 的陣列成,這種會減少(排序一下,小的數到它前面了),也是要計算的。
-
第三種逆序對,是一個數 \(n+1\) 和大於 \(n+1\) 的陣列成,這可能會改變,但是操作過後每個數 \(n+1\) 的位置上會變為小於等於自己的數,和大於 \(n+1\) 組成的逆序對個數一樣,因此,並未改變。
-
第四種逆序對,是兩個大於 \(n+1\) 的陣列成,顯然個數不變。
理清了思路,發現第二種逆序對正好可以用樹狀陣列動態解決,操作 \(n\)
並且注意,它的操作是會延續到下一次的,因此注意下下一次操作小於本次操作。
時間複雜度 \(\texttt{O(N logN)}\)。
注意會暴 \(\texttt{int}\)。
程式碼
#include <bits/stdc++.h> using namespace std; #define PB push_back #define int long long const int N = 1e6; inline int read() { int s = 0; register bool neg = 0; register char c = getchar(); for (; c < '0' || c > '9'; c = getchar()) neg |= (c == '-'); for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar()) ; s = (neg ? -s : s); return s; } int a, b, s[N + 10], p[N + 10], top, Ans[N + 10], q[N + 10], t[N + 10]; vector<int> id[N + 10]; inline void add(int n, int m) { for (; n <= a; n += n & -n) q[n] += m; } inline int query(int n) { int ans = 0; for (; n; n -= n & -n) ans += q[n]; return ans; } signed main() { a = read(); b = read(); for (int i = 1; i <= a; i++) s[i] = p[i] = read(); sort(p + 1, p + a + 1); top = unique(p + 1, p + a + 1) - p - 1; for (int i = 1; i <= a; i++) s[i] = lower_bound(p + 1, p + top + 1, s[i]) - p; int ans = 0, qq = 0; for (int i = 1; i <= a; i++) { add(s[i], 1);//求一開始的逆序對。 qq += t[s[i]]; ans += i - query(s[i]); t[s[i]]++; } memset(q, 0, sizeof(q)); ans += qq; printf("%lld\n", ans); for (int i = 1; i <= a; i++) id[s[i]].PB(i); int sum = 0; for (int i = 1; i <= top; i++) { for (int j = 0; j < id[i].size(); j++) { ans -= sum - query(id[i][j]); } ans -= id[i].size() - 1;//應對題目奇奇怪怪的逆序對定義。 for (int j = 0; j < id[i].size(); j++) { add(id[i][j], 1);//維護小於等於n個數的字首 Ans[id[i][j]] = ans;//儲存答案 } sum += id[i].size(); } int x; int mx = 1e18; for (int i = 1; i <= b; i++) { x = read(); mx = min(mx, Ans[x]); printf("%lld\n", mx); } return 0; }