当前位置: 首页 > news >正文

题解:P12485 [集训队互测 2024] PM 大师

写这个题跟用户名没啥关系。

\(a_i=0\) 的位置为 \(c_1,\cdots,c_m\)\(pre_i\)\(a_{1\sim i}\)\(0\) 的个数。可以发现 \(b_{c_i}\) 是严格单调递增的,设其中的数值构成的集合为 \(S\)

从值域考虑,不难发现对于非 \(0\)\(a_i=v\),我们只关心其第一次出现的位置 \(p_v\),设 \(d_v=pre_{p_v}\)(若没有出现 \(v\)\(d_v=m\))。按值域从小到大枚举 \(v=1\sim n\),设 \(s_v\)\(1\sim v-1\)\(\notin S\) 的数的个数,则

\[v\notin S\Leftrightarrow v-d_v-s_v>0 \]

查询 \(a_y\) 时,若 \(a_y=0\),则相当于查询第 \(pre_y\) 小的 \(S\) 中的数。在值域上开一棵线段树,区间内维护 \(v-d_v-s_v\leq 0\) 的数的个数,线段树二分即可得到结果。

考虑如何处理修改。每次将 \(a_x\) 修改为 \(a_x'\) 后,\(d_{a_x}\)\(d_{a_x'}\) 都可能变化。这相当于做两次对 \(v-d_v-s_v\) 的单点修改。考察某次单点修改带来的变化,若原先 \(v-d_v-s_v>0\),修改后 \(v-d_v-s_v\leq 0\),则 \(v\) 插入 \(S\) 中,\(v+1\sim n\) 中的值的 \(s_v\) 都会减 \(1\)。注意到,这可能导致 \(v+1\sim n\) 中的某些值的 \(v-d_v-s_v\)\(\leq 0\) 变成 \(>0\),此时我们取最小的这样的数 \(v_{\min}\),则 \(v_{\min}\) 将从 \(S\) 中删除,删除后更大的值就恢复正常了。另一种情况也是对称的。

同样用线段树维护,区间内维护 \(\in S\) 的值的最大值及其位置,和 \(\notin S\) 的值的最小值及其位置,还有加法标记即可。\(p_v\) 可以用 set 或者懒删除的堆维护。时间复杂度为 \(\mathcal{O}((n+q)\log{n})\)

主要代码
int n, q, a[MAXN], pre[MAXN], val[MAXN];
bool vis[MAXN];
priority_queue<int, vector<int>, greater<>> Q[MAXN];struct SegTree {
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)struct Node {int mn, mnp, mx, mxp, cnt;Node &operator+=(const Node &rhs) {if (rhs.mn < mn) {mn = rhs.mn;mnp = rhs.mnp;}if (rhs.mx > mx) {mx = rhs.mx;mxp = rhs.mxp;}cnt += rhs.cnt;return *this;}friend Node operator+(Node lhs, const Node &rhs) {return lhs += rhs;}} nd[MAXN << 2];int tg[MAXN << 2];void build(int p, int l, int r) {if (l == r) {nd[p] = {inf, 0, -inf, 0, 0};if (val[l] > 0) {vis[l] = false;nd[p].mn = val[l];nd[p].mnp = l;} else {vis[l] = true;nd[p].mx = val[l];nd[p].mxp = l;nd[p].cnt = 1;}return;}int mid = l + r >> 1;build(ls(p), l, mid);build(rs(p), mid + 1, r);pushUp(p);}void pushUp(int p) {nd[p] = nd[ls(p)] + nd[rs(p)];}void applyAdd(int p, int v) {tg[p] += v;if (nd[p].mn != inf) nd[p].mn += v;if (nd[p].mx != -inf) nd[p].mx += v;}void pushDown(int p) {if (!tg[p]) return;applyAdd(ls(p), tg[p]);applyAdd(rs(p), tg[p]);tg[p] = 0;}int query(int p, int l, int r, int x) {if (l == r) return vis[l] ? nd[p].mx : nd[p].mn;pushDown(p);int mid = l + r >> 1;return x <= mid ? query(ls(p), l, mid, x) : query(rs(p), mid + 1, r, x);}void upd(int p, int l, int r, int x) {if (l == r) {if (nd[p].mn <= 0) {nd[p].mx = nd[p].mn;nd[p].mn = inf;swap(nd[p].mnp, nd[p].mxp);} else if (nd[p].mx > 0) {nd[p].mn = nd[p].mx;nd[p].mx = -inf;swap(nd[p].mnp, nd[p].mxp);}nd[p].cnt = vis[l];return;}pushDown(p);int mid = l + r >> 1;if (x <= mid) upd(ls(p), l, mid, x);else upd(rs(p), mid + 1, r, x);pushUp(p);}void add(int p, int l, int r, int x, int y, int v) {if (x <= l && y >= r) {applyAdd(p, v);return;}pushDown(p);int mid = l + r >> 1;if (x <= mid) add(ls(p), l, mid, x, y, v);if (y > mid) add(rs(p), mid + 1, r, x, y, v);pushUp(p);}int find(int p, int l, int r, int k) {if (l == r) return l;pushDown(p);int mid = l + r >> 1;return k <= nd[ls(p)].cnt ? find(ls(p), l, mid, k) : find(rs(p), mid + 1, r, k - nd[ls(p)].cnt);}
#undef ls
#undef rs
} sgt;int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cin >> n >> q;for (int i = 1; i <= n; ++i) {cin >> a[i];pre[i] = pre[i - 1] + (a[i] == 0);}int m = pre[n];for (int v = 1, cnt = 0; v <= n; ++v) {val[v] = v - m - cnt;if (val[v] > 0) ++cnt;}sgt.build(1, 1, n);while (q--) {int x, k, y;cin >> x >> k >> y;auto getD = [&](int v) {if (v <= 0) return -1;while (!Q[v].empty() && a[Q[v].top()] != v) Q[v].pop();return Q[v].empty() ? m : pre[Q[v].top()];};auto upd = [&](int v, int dt) {int prvVal = sgt.query(1, 1, n, v), nxtVal = prvVal + dt;sgt.add(1, 1, n, v, v, dt);if (prvVal > 0 && nxtVal <= 0) {vis[v] = true;sgt.upd(1, 1, n, v);if (v < n) {sgt.add(1, 1, n, v + 1, n, 1);if (sgt.nd[1].mx > 0) {int p = sgt.nd[1].mxp;vis[p] = false;sgt.upd(1, 1, n, p);if (p < n) sgt.add(1, 1, n, p + 1, n, -1);}}} else if (prvVal <= 0 && nxtVal > 0) {vis[v] = false;sgt.upd(1, 1, n, v);if (v < n) {sgt.add(1, 1, n, v + 1, n, -1);if (sgt.nd[1].mn <= 0) {int p = sgt.nd[1].mnp;vis[p] = true;sgt.upd(1, 1, n, p);if (p < n) sgt.add(1, 1, n, p + 1, n, 1);}}}};int tmp = a[x], prv1 = getD(tmp), prv2 = getD(k);a[x] = k;if (k > 0) Q[k].emplace(x);int nxt1 = getD(tmp), nxt2 = getD(k);if (tmp > 0) upd(tmp, prv1 - nxt1);if (k > 0) upd(k, prv2 - nxt2);cout << (a[y] ? a[y] : sgt.find(1, 1, n, pre[y])) << '\n';}return 0;
}
http://www.rkmt.cn/news/1481023.html

相关文章:

  • AppImageLauncher:告别Linux软件安装烦恼,双击即可运行AppImage应用 [特殊字符]
  • Python MIDI处理实战指南:Mido库深度解析与应用
  • STM32低功耗调试:解决STOP模式调试失效的DBGMCU配置指南
  • 英雄联盟终极自动化工具:League Akari完全使用指南
  • 【AI驱动的选题决策系统】:CSDN 237万条营销数据反哺内容策略的5大闭环验证模型
  • iOS激活锁绕过技术方案解析:applera1n的内存级安全绕过机制
  • 破解自动化间歇运动痛点:凸轮分割器四精协同方法论如何实现高精度低成本? - 速递信息
  • STM32驱动74HC595级联控制数码管的实用代码包,含中文注释与引脚配置说明
  • 深入解析LabVIEW内存数据布局:从基础类型到复杂结构的内存模型与实战应用
  • FitGirl游戏启动器完全指南:一站式管理压缩游戏的终极解决方案
  • 2026年微信小程序怎么弄出来 - 凡科杰建云
  • 在Mac上运行Windows软件的最简单方法:Whisky完整使用指南
  • C++实现的VIBE+卡尔曼滤波多目标跟踪系统(含匈牙利匹配与背景减除)
  • 2026年国内防水背衬板厂家排行 靠谱品牌实力实测对比:优选廊坊永玖节能科技有限公司 - 奔跑123
  • 【JVM】编译解释
  • 2026年6月市场有实力的真空计销售商推荐,氦质谱检漏仪/真空计/真空泵,真空计销售商哪家专业 - 品牌推荐师
  • 精通BambuStudio开发:从源码构建到高级定制实战指南
  • 终极免费字体解决方案:如何用Montserrat字体家族提升你的设计品质
  • 2026年最新:国内泡沫玻璃板/泡沫玻璃管厂家综合实力排行 推荐欧诗德(天津)节能科技有限公司 - 奔跑123
  • 在Windows电脑上3步安装Coolapk UWP桌面版:告别手机小屏幕,享受大屏酷安体验
  • 002-2026年微信小程序怎么做自己的店铺-图文版-2026-06-07 - 凡科杰建云
  • 手把手教你用SimpleUI美化Django Admin:定制Logo、菜单与主题的完整实战
  • 列车通过桥梁时梁体动态响应MATLAB仿真工具包(含动图可视化)
  • FlicFlac:Windows上最轻量的免费音频格式转换神器
  • 终极指南:免费为Mac解锁NTFS完整读写权限
  • 华为云Agentic Infra:企业级AI基础设施新范式的深度解析
  • Windows和Office激活终极指南:3分钟完成永久免费激活的完整解决方案
  • 3分钟解锁AI图像分层:告别繁琐手工,拥抱智能设计新纪元
  • 中国芯片设计业的创新共识:从成本优化到价值创造的演进之路
  • 3分钟掌握百度网盘秒传脚本:永久分享文件的完整终极指南