CF1858E1 做题笔记
赛时没做出来,晚上补了一下,发现是一种很好玩的 数据结构。
由于可以离线又要支持删除后
对序列按照时间建成一颗操作树,处于某个点的回合时,这个序列的样子就是它以及它的祖先。
来依次考虑某个操作,设当前是序列的末尾是
加操作,直接在
减操作,此时我们发现需要跳
撤销操作,其实可以实时记录加减操作的一个栈,这个时候把栈顶弹出,把
询问操作,留到最后建完表达式树再做。
我们发现询问就是问每个点向上构成的序列中的元素个数,可以用类似莫队的方法解决,但是这一部分是
#include <bits/stdc++.h> #define For(i, a, b) for (int i = (a); i <= (b); i ++) #define foR(i, a, b) for (int i = (a); i >= (b); i --) using namespace std; stack <int> st; int p, q, x, cnt, res; int f[1000005][20], val[1000005]; int b[1000005], ans[1000005], pos[1000005]; char op[1000005]; vector <int> G[1000005]; void add (int x) { b[x] ++; if (b[x] == 1) ++ res; } void rem (int x) { b[x] --; if (b[x] == 0) -- res; } void dfs (int u) { if (u) add (val[u]); ans[u] = res; for (int v : G[u]) dfs (v); if (u) rem (val[u]); } signed main () { st.push (0); scanf ("%d", &q); For (i, 1, q) { op[i] = getchar (); while (op[i] == ' ' || op[i] == '\n') op[i] = getchar (); if (op[i] == '+') { scanf ("%d", &x); f[++ cnt][0] = p; val[cnt] = x; For (j, 1, 20) f[cnt][j] = f[f[cnt][j - 1] ][j - 1]; p = cnt; } else if (op[i] == '-') { scanf ("%d", &x); foR (j, 20, 0) { if (x >= (1 << j) ) { x -= (1 << j); p = f[p][j]; } } } else if (op[i] == '!') { st.pop (); p = st.top (); } pos[i] = p; if (op[i] != '?' && op[i] != '!') st.push (p); } For (i, 1, cnt) G[f[i][0] ].push_back (i); dfs (0); For (i, 1, q) if (op[i] == '?') cout << ans[pos[i] ] << '\n'; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异