人人本着希望之名
望月悲叹的最初分块
严格强于区间 rank, 直接考虑分块.
平凡的 做法缺点在于二分这个东西完全用不到分块容易预处理的优秀性质. 本题的值域很小, 二分直接扔掉.
相比较而言, 值域分块和序列分块契合度更高, 考虑值域分块.
设 表示前 块有 个数值为 , 表示前 块有 个数值域在第 块.
查询时先把散块的信息算出来, 然后在值域块上跳, 单次查询块内信息是 的, 整体复杂度 .
然后考虑修改. 发现这个修改严格弱于 loj516, 散块暴力重构, 整块打 tag. 整体的复杂度还是 . 代码还没有, 别急. 有代码了.
#include <bits/stdc++.h> #define pb emplace_back #define fir first #define sec second using i64 = long long; using pii = std::pair<int, int>; const int maxn = 1e5 + 5; const int T = 330; int n, m, S, V, mx, a[maxn], t, vt, L[T], R[T], pos[maxn], vL[T], vR[T]; int vpos[maxn], val[maxn], pre[maxn], rec[T][maxn], g[T][maxn], G[T][maxn]; struct Query { int op, l, r, x, y; Query() { op = l = r = x = y = 0; } Query(int op, int l, int r, int x = 0, int y = 0) : op(op), l(l), r(r), x(x), y(y) {} } Q[maxn]; int read() { int s = 0; char c = getchar(); for(;c < '0'||c > '9';c = getchar()); for(;c >= '0'&&c <= '9';c = getchar()) s = (s << 1) + (s << 3) + (c ^ '0'); return s; } void write(int x) { if(x > 9) write(x / 10); putchar(x % 10 + '0'); return ; } int find(int x) { return x == pre[x] ? x : pre[x] = find(pre[x]); } void merge(int blo, int x, int y) { if(!rec[blo][x]) return ; if(!rec[blo][y]) val[rec[blo][y] = rec[blo][x]] = y; else pre[rec[blo][x]] = pre[rec[blo][y]]; return rec[blo][x] = 0, void(); } void rebuild(int blo, int l, int r, int x, int y) { for(int i = L[blo];i <= R[blo];++ i) a[i] = val[find(i)], rec[blo][a[i]] = 0; for(int i = l;i <= r;++ i) if(a[i] == x) a[i] = y; for(int i = L[blo];i <= R[blo];++ i) { if(!rec[blo][a[i]]) rec[blo][a[i]] = pre[i] = i, val[i] = a[i]; else pre[i] = rec[blo][a[i]]; } return ; } void modify(int l, int r, int x, int y) { if(x == y) return ; const int p = pos[l], q = pos[r], vpx = vpos[x], vpy = vpos[y]; int sum = 0, tmp = 0; if(p == q) { for(int i = l;i <= r;++ i) sum += val[find(i)] == x; if(!sum) return ; for(int i = p;i <= t;++ i) g[i][x] -= sum, G[i][vpx] -= sum, g[i][y] += sum, G[i][vpy] += sum; return rebuild(p, l, r, x, y); } for(int i = l;i <= R[p];++ i) sum += val[find(i)] == x; if(sum) rebuild(p, l, R[p], x, y); for(int i = p;i < q;++ i) sum += tmp, G[i][vpx] -= sum, G[i][vpy] += sum, tmp = g[i + 1][x] - g[i][x], g[i][x] -= sum, g[i][y] += sum; tmp = 0; for(int i = L[q];i <= r;++ i) if(val[find(i)] == x) ++ sum, ++ tmp; for(int i = q;i <= t;++ i) G[i][vpx] -= sum, G[i][vpy] += sum, g[i][x] -= sum, g[i][y] += sum; for(int i = p + 1;i < q;++ i) merge(i, x, y); if(tmp) rebuild(q, L[q], r, x, y); return ; } int buc[maxn], vbuc[maxn]; int query(int l, int r, int k) { const int p = pos[l], q = pos[r]; if(k > r - l + 1) return -1; if(p == q) { for(int i = l, x;i <= r;++ i) ++ buc[x = val[find(i)]], ++ vbuc[vpos[x]]; for(int blo = 1;blo <= vt;++ blo) { if(k > vbuc[blo]) k -= vbuc[blo]; else for(int z = vL[blo];z <= vR[blo];++ z) { if(k > buc[z]) k -= buc[z]; else { for(int i = l, x;i <= r;++ i) -- buc[x = val[find(i)]], -- vbuc[vpos[x]]; return z; } } } return 0; } for(int i = l, x;i <= R[p];++ i) ++ buc[x = val[find(i)]], ++ vbuc[vpos[x]]; for(int i = L[q], x;i <= r;++ i) ++ buc[x = val[find(i)]], ++ vbuc[vpos[x]]; for(int blo = 1;blo <= vt;++ blo) { if(k > G[q - 1][blo] - G[p][blo] + vbuc[blo]) k -= G[q - 1][blo] - G[p][blo] + vbuc[blo]; else for(int z = vL[blo];z <= vR[blo];++ z) { if(k > g[q - 1][z] - g[p][z] + buc[z]) k -= g[q - 1][z] - g[p][z] + buc[z]; else { for(int i = l, x;i <= R[p];++ i) -- buc[x = val[find(i)]], -- vbuc[vpos[x]]; for(int i = L[q], x;i <= r;++ i) -- buc[x = val[find(i)]], -- vbuc[vpos[x]]; return z; } } } return 0; } int main() { n = read(), m = read(); S = std::sqrt(n); t = (n - 1) / S + 1; for(int i = 1;i <= t;++ i) L[i] = R[i - 1] + 1, R[i] = R[i - 1] + S; R[t] = n; for(int i = 1;i <= t;++ i) for(int j = L[i];j <= R[i];++ j) { V = std::max(V, a[j] = read()); if(!rec[i][a[j]]) pre[j] = rec[i][a[j]] = j, val[j] = a[j]; else pre[j] = rec[i][a[j]]; pos[j] = i; } for(int i = 1;i <= m;++ i) { Q[i].op = read(); Q[i].l = read(); Q[i].r = read(); Q[i].x = read(); if(Q[i].op == 1) Q[i].y = read(), V = std::max(V, Q[i].x), V = std::max(V, Q[i].y); } mx = V; V = std::sqrt(V); vt = (mx - 1) / V + 1; for(int i = 1;i <= vt;++ i) vL[i] = vR[i - 1] + 1, vR[i] = vR[i - 1] + V; vR[vt] = mx; for(int i = 1;i <= vt;++ i) for(int j = vL[i];j <= vR[i];++ j) vpos[j] = i; for(int i = 1;i <= t;++ i) { for(int j = L[i];j <= R[i];++ j) ++ g[i][a[j]], ++ G[i][vpos[a[j]]]; for(int j = 1;j <= mx;++ j) { g[i][j] += g[i - 1][j]; if(j <= vt) G[i][j] += G[i - 1][j]; } } for(int i = 1;i <= m;++ i) if(Q[i].op == 1) modify(Q[i].l, Q[i].r, Q[i].x, Q[i].y); else write(query(Q[i].l, Q[i].r, Q[i].x)), putchar('\n'); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】