传送门:点击打开链接
题意:两种操作,操作1查询区间第k大,操作2把位置x的值改成y。n,Q<=1e5
思路:整体二分。刚开始以为是在线段树上二分答案,没想到它竟然是对所有的二分答案,还是第一次见到这样的二分方法!
说下整体二分的大概思路:
首先,按照操作顺序,将所有的查询和修改操作全部加入到数组A中,然后去调用solve函数,L和R其实是数组A的指针,l和r是二分答案的范围
之后,取l和r的中点m,然后顺序遍历数组A中所有的操作,该修改的在树状数组上修改,该查询的在树状数组上查询。
之后,把数组A给划分开,划分成前l1部分的操作位置的数都<=m,后l2部分的操作位置的数都>m,划分完后,分开递归,并缩小答案范围。
最后总复杂度是,整体二分的复杂度是O(nlogn),树状数组的复杂度是O(logn),合起来一共是O(nlogn^2)
还有一个技巧,就是树状数组上直接打标记,来快速清空数组
#include <map> #include <set> #include <cmath> #include <ctime> #include <stack> #include <queue> #include <cstdio> #include <cctype> #include <string> #include <vector> #include <cstring> #include <iomanip> #include <iostream> #include <algorithm> #include <functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; typedef pair<int, int> PII; const int MX = 4e5 + 5; const int INF = 0x3f3f3f3f; int sum[MX], flag[MX], n, DFN; int val[MX], ans[MX], tmp[MX]; struct Data { int op, id, x, y, k, cnt; Data() {} Data(int _op, int _id, int _x, int _y, int _k) { op = _op; id = _id; x = _x; y = _y; k = _k; } } A[MX], T1[MX], T2[MX]; void add(int x, int y, int id) { for(int i = x; i <= n; i += i & -i) { if(flag[i] != id) flag[i] = id, sum[i] = 0; sum[i] += y; } } int ask(int x, int id) { int ret = 0; for(int i = x; i; i -= i & -i) { if(flag[i] == id) ret += sum[i]; } return ret; } void solve(int L, int R, int l, int r) { if(L > R) return; if(l == r) { for(int i = L; i <= R; i++) { if(A[i].op == 3) ans[A[i].id] = l; } return; } int m = (l + r) >> 1; DFN++; for(int i = L; i <= R; i++) { if(A[i].op == 1 && A[i].y <= m) add(A[i].x, 1, DFN); if(A[i].op == 2 && A[i].y <= m) add(A[i].x, -1, DFN); if(A[i].op == 3) tmp[i] = ask(A[i].y, DFN) - ask(A[i].x - 1, DFN); } int l1 = 0, l2 = 0; for(int i = L; i <= R; i++) { if(A[i].op == 3) { if(A[i].cnt + tmp[i] >= A[i].k) T1[++l1] = A[i]; else A[i].cnt += tmp[i], T2[++l2] = A[i]; } else if(A[i].y <= m) T1[++l1] = A[i]; else T2[++l2] = A[i]; } for(int i = 1; i <= l1; i++) A[L + i - 1] = T1[i]; for(int i = 1; i <= l2; i++) A[L + l1 + i - 1] = T2[i]; solve(L, L + l1 - 1, l, m); solve(L + l1, R, m + 1, r); } int main() { //FIN; while(~scanf("%d", &n)) { int sz = 0, Q, qsz = 0, Max = 0; DFN = 0; memset(sum, 0, sizeof(sum)); for(int i = 1; i <= n; i++) { scanf("%d", &val[i]); A[++sz] = Data(1, -1, i, val[i], -1); Max = max(Max, val[i]); } scanf("%d", &Q); for(int i = 1; i <= Q; i++) { int op, x, y, k; scanf("%d%d%d", &op, &x, &y); if(op == 1) { A[++sz] = Data(2, -1, x, val[x], -1); A[++sz] = Data(1, -1, x, y, -1); Max = max(Max, y); val[x] = y; } else { qsz++; scanf("%d", &k); A[++sz] = Data(3, qsz, x, y, k); A[sz].cnt = 0; } } solve(1, sz, 0, Max); for(int i = 1; i <= qsz; i++) { printf("%d\n", ans[i]); } } return 0; }