洛谷 P2824 [HEOI2016/TJOI2016]排序
阿新 • • 發佈:2021-10-14
Description
Solution
一眼就能想到線段樹,但是線段樹似乎也無法維護啊?那怎麼做?
於是我們檢視標籤……
發現……二分答案?這怎麼二分答案??
於是我們再回想一下題目要求計算什麼:位置是 \(q\) 的數是多少。
而且只有一次詢問。
既然是二分答案,那我們就二分唄。
假設答案是 \(mid\),那現在的問題就是判斷 \(mid\) 是不是操作完之後是第 \(q\) 個數。
考慮到線段樹只能進行區間維護,比如區間加,區間修改,區間求和什麼的……
誒,等等,如果我們把小於 \(mid\) 的數都重新賦值為 0,大於等於 \(mid\) 的數都賦值為 1,那這個排序是不是就相當於區間賦值了呢?對於從小到大排序,我們不需要知道數到底是多少,只需要把 0 全都放到區間的前半段,1 放到後半段即可。反之同理。
操作完之後,查詢第 \(q\) 位是 0 還是 1,如果是 0,說明這個 \(mid\) 太大了,需要調小,反之則需要調大。
這樣一來,這道題就可以完美的解決了,時間複雜度 \(O(nlog^2_n)\)。
最後再講一下如何區間修改:我們記錄一下區間和,那麼這個區間和就是區間裡 1 的個數,每次修改時先查詢一下區間和,然後覆蓋即可,具體看程式碼吧。
Code
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #define ls rt << 1 #define rs rt << 1 | 1 using namespace std; inline int read(){ int x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x; } const int N = 1e5 + 10; int n, m, maxs, p, ans; int a[N], sum[N << 2], lazy[N << 2]; bool b[N]; struct node{ int op, l, r; }q[N]; inline void pushup(int rt){ sum[rt] = sum[ls] + sum[rs]; } inline void pushdown(int l, int r, int rt){ if(lazy[rt] != -1){ int mid = (l + r) >> 1; sum[ls] = lazy[rt] * (mid - l + 1); sum[rs] = lazy[rt] * (r - mid); lazy[ls] = lazy[rs] = lazy[rt]; lazy[rt] = -1; } } inline void build(int l, int r, int rt){ lazy[rt] = -1; if(l == r){ sum[rt] = b[l]; return; } int mid = (l + r) >> 1; build(l, mid, ls); build(mid + 1, r, rs); pushup(rt); } inline int query(int L, int R, int l, int r, int rt){ if(L <= l && r <= R) return sum[rt]; pushdown(l, r, rt); int mid = (l + r) >> 1; int res = 0; if(L <= mid) res += query(L, R, l, mid, ls); if(R > mid) res += query(L, R, mid + 1, r, rs); pushup(rt); return res; } inline void update(int L, int R, int k, int l, int r, int rt){ if(L <= l && r <= R){ sum[rt] = k * (r - l + 1); lazy[rt] = k; return; } pushdown(l, r, rt); int mid = (l + r) >> 1; if(L <= mid) update(L, R, k, l, mid, ls); if(R > mid) update(L, R, k, mid + 1, r, rs); pushup(rt); } inline bool check(int mid){ for(int i = 1; i <= n; i++) b[i] = a[i] >= mid; build(1, n, 1); for(int i = 1; i <= m; i++){ int s = query(q[i].l, q[i].r, 1, n, 1); if(!s || s == q[i].r - q[i].l + 1) continue; if(!q[i].op) update(q[i].l, q[i].r - s, 0, 1, n, 1), update(q[i].r - s + 1, q[i].r, 1, 1, n, 1); else update(q[i].l, q[i].l + s - 1, 1, 1, n, 1), update(q[i].l + s, q[i].r, 0, 1, n, 1); } return query(p, p, 1, n, 1); } int main(){ n = read(), m = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i <= m; i++) q[i].op = read(), q[i].l = read(), q[i].r = read(); p = read(); int l = 1, r = n; while(l <= r){ int mid = (l + r) >> 1; if(check(mid)) ans = mid, l = mid + 1; else r = mid - 1; } printf("%d\n", ans); return 0; }
End
本文來自部落格園,作者:{xixike},轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15407265.html