当前位置: 首页 > 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.jsqmd.com/news/969304/

相关文章:

  • Cursor Free VIP:3分钟高效解决AI编程助手使用限制的完整方案
  • AppImageLauncher:告别Linux软件安装烦恼,双击即可运行AppImage应用 [特殊字符]
  • 2026年深圳AI豆包GEO营销推广公司推荐:6家头部服务商深度测评 - 猫头鹰AI推广
  • Windows 10系统优化终极指南:一键清理预装软件提升性能
  • Python MIDI处理实战指南:Mido库深度解析与应用
  • 【AI驱动的选题决策系统】:CSDN 237万条营销数据反哺内容策略的5大闭环验证模型
  • 高效JSXBIN反编译器:自动化解密Adobe脚本的智能解决方案
  • 2026惠州黄金回收避坑指南:惠奢汇惠城旗舰店领衔,全城靠谱门店TOP5推荐 - 生活测评小能手
  • 英雄联盟终极自动化工具:League Akari完全使用指南
  • STM32低功耗调试:解决STOP模式调试失效的DBGMCU配置指南
  • 汕头高铁站酒店|艺龙玺程国际酒店,汕头站旁真实住体验 - 资讯纵览
  • 达芬奇DM6446:异构计算软件框架的早期探索与实战挑战
  • iOS激活锁绕过技术方案解析:applera1n的内存级安全绕过机制
  • 3分钟掌握Kobi漫画阅读器:跨平台漫画阅读新体验
  • 终极图片去重解决方案:3步让10000张照片秒变有序的完整指南
  • Node-RED仪表板开发:5个步骤构建专业数据可视化界面
  • 如何系统掌握网络压力测试:3大实战技巧与LOIC工具完整解析
  • 2026年国内防水背衬板靠谱厂家排行 资质与实测案例对标:推荐廊坊永玖节能科技有限公司 - 奔跑123
  • 2026最强Java八股文:万字总结+全答案,从JVM到高并发,一篇干翻所有面试
  • 破解自动化间歇运动痛点:凸轮分割器四精协同方法论如何实现高精度低成本? - 速递信息
  • STM32驱动74HC595级联控制数码管的实用代码包,含中文注释与引脚配置说明
  • FPGA驱动TLC549 ADC芯片:Verilog代码实现与调试经验分享
  • 【小程序】基于 AI 大语言模型驱动的中国古典诗词 Web 应用详细设计指南
  • SheetJS:如何构建企业级无依赖电子表格处理架构?
  • 2026年门店小程序怎么开通 - 凡科杰建云
  • 深入解析LabVIEW内存数据布局:从基础类型到复杂结构的内存模型与实战应用
  • 德州仪器65亿美元收购国半:模拟芯片行业整合与新能源战略布局
  • FitGirl游戏启动器完全指南:一站式管理压缩游戏的终极解决方案
  • CSDN AI数字营销升级全流程图谱:含合同条款比对表、API权限继承逻辑、历史数据迁移SOP
  • 搬家货物怎么寄?大件物流+小件快递一站式寄送省钱方案 - 时讯资讯