CF785E (動態逆序對)
阿新 • • 發佈:2021-11-09
題意描述
對長度為\(n\)的排列進行\(k\)次操作,每次操作交換兩個數字,求每次交換以後,整個序列的逆序對數量
思路
\(1.\) 分塊
對於查詢和修改,我們可以直接在塊內暴力解決。
觀察發現,如果交換\(a[l]\)和\(a[r]\),則對\([l + 1, r - 1]\)區間內有影響的數的值域在\([a[l],a[r]]\)之間。
因為上面的性質,對於整塊我們要快速找到值域在\([a[l],a[r]]\)的數,所以對於每個塊我們可以維護一個有序的vector,這樣查詢操作就可以快速解決。
對於修改來說,我們直接在對應的塊內暴力修改,修改完後一定要對vector排序
對於查詢來說,非整塊可以直接暴力找值域在\([a[l],a[r]]\)
總複雜度為\(O(q\sqrt{n}logn)\)
\(2.\) 樹套樹
待補
程式碼
#include <bits/stdc++.h> #define all(x) x.begin(), x.end() #define sz(x) (int)x.size() using ll = long long; using pii = std::pair<int, int>; const int MOD = 1e9 + 7; const int N = 2 * 1e5 + 5; const int INF = 0x3f3f3f3f; int a[N], n, k, block; std::vector<int> g[N]; void init() { block = sqrt(n + 0.5); for(int i = 1; i <= n; ++i) { a[i] = i; g[i / block].push_back(a[i]); } } ll query(int l, int r) { int idl = l / block, idr = r / block; int x = a[l], y = a[r]; int k = std::lower_bound(g[idl].begin(), g[idl].end(), x) - g[idl].begin(); g[idl][k] = y; std::sort(g[idl].begin(), g[idl].end()); k = std::lower_bound(g[idr].begin(), g[idr].end(), y) - g[idr].begin(); g[idr][k] = x; std::sort(g[idr].begin(), g[idr].end()); ll ans = 0, f; // 特判符號位 if(x > y) f = -1, std::swap(x, y); else f = 1; if(idl == idr) { for(int i = l; i <= r; ++i) { if(x < a[i] && a[i] < y) ++ans; } } else { int i = l, j = r; while(i / block == l / block) { if(x < a[i] && a[i] < y) { ++ans; } ++i; } while(j / block == r / block) { if(x < a[j] && a[j] < y) { ++ans; } --j; } for(int k = i / block; k <= j / block; ++k) { ans += std::upper_bound(g[k].begin(), g[k].end(), y) - std::lower_bound(g[k].begin(), g[k].end(), x); } } std::swap(a[l], a[r]); return f * (2 * ans + 1); } void solve() { std::cin >> n >> k; init(); ll ans = 0; while(k--) { int l, r; std::cin >> l >> r; if(l > r) std::swap(l, r); if(l != r) ans += query(l, r); std::cout << ans << '\n'; } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(0); std::cout.tie(0); std::cout << std::fixed << std::setprecision(8); int T = 1; // std::cin >> T; while(T--) solve(); }