namespace SGT {
int pond = ...;
int nClock, root[N];
structnode {int lc, rc;
int cnt;
} t[pond];
voidinsert(int &p, int q, int l, int r, int x, int val) {
p = ++ nClock, t[p] = t[q];
t[p].cnt += val;
if (l == r) return;
int mid = (l + r) >> 1;
if (x <= mid)
insert(t[p].lc, t[q].lc, l, mid, x, val);
else
insert(t[p].rc, t[q].rc, mid + 1, r, x, val);
}
}
namespace SGT {
constint pond = ...;
int nClock, root[N];
structnode {int lc, rc;
int sum;
int add;
} t[pond];
voidchange(int &p, int q, int l, int r, int s, int e, int val) {
p = ++ nClock, t[p] = t[q];
if (s <= l && r <= e) { t[p].add += val; return; }
t[p].sum += (std::min(r, e) - std::max(l, s) + 1) * val;
int mid = (l + r) >> 1;
if (s <= mid)
change(t[p].lc, t[q].lc, l, mid, s, e, val);
if (mid < e)
change(t[p].rc, t[q].rc, mid + 1, r, s, e, val);
}
intask(int p, int l, int r, int s, int e) {
if (s <= l && r <= e)
return t[p].sum + (r - l + 1) * t[p].add;
int mid = (l + r) >> 1;
int cur = (std::min(r, e) - std::max(l, s) + 1) * t[p].add;
if (s <= mid)
cur += ask(t[p].lc, l, mid, s, e);
if (mid < e)
cur += ask(t[p].rc, mid + 1, r, s, e);
return cur;
}
}
namespace SGT {
int dep[N * 4];
int idx[N];
int f[logN][N];
voidbuild(int p, int l, int r) {
dep[p] = dep[p / 2] + 1;
int mid = (l + r) >> 1;
// Calculate the data of the interval [l, mid]// Calculate the data of the interval [mid + 1, r]if (l == r) idx[l] = p;
else build(p * 2, l, mid), build(p * 2 + 1, mid + 1, r);
}
intask(int l, int r) {
int x = idx[l], y = idx[r];
if (dep[x] > dep[y]) std::swap(x, y);
while (dep[x] < dep[y]) y /= 2;
while (x ^ y) x /= 2, y /= 2;
return ...;
}
}
namespace SGT {
constint H = ...;
int h, logx[H];
int idx[H];
int f[logH][H];
voidinit() {
h = 1;
while (h < n) h <<= 1;
logx[0] = 0;
for (int i = 1; i <= h; i ++) logx[i] = logx[i >> 1] + 1;
}
voidbuild(int p, int l, int r, int dep) {
int mid = (l + r) >> 1;
// Calculate the data of the interval [l, mid]// Calculate the data of the interval [mid + 1, r]if (l == r) idx[l] = p;
else build(p * 2, l, mid, dep + 1), build(p * 2 + 1, mid + 1, r, dep + 1);
}
intask(int l, int r) {
int x = idx[l], y = idx[r];
int z_dep = logx[x] - logx[x ^ y];
return ...;
}
}
int n, Bn;
int t, Lp[Size_Bn], Rp[Size_Bn], belong[N];
intmain() {
for (int i = Bn; i <= n; i += Bn) t ++, Lp[t] = i - Bn + 1, Rp[t] = i;
if (Rp[t] < n) t ++, Lp[t] = Rp[t - 1] + 1, Rp[t] = n;
for (int i = 1; i <= n; i ++) belong[i] = (i - 1) / Bn + 1;
}
当遇到单点修改、区间查询时,可以考虑用分块优化到 O(1) 修改 O(√n) 查询。
当遇到区间修改、单点查询时,可以考虑用分块优化到 O(√n) 修改 O(1) 查询。
当遇到区间修改、区间查询时,可以考虑用差分转化为单点修改、区间查询。
当遇到限定 i→i+ki,询问从 x 出发首次 ≥y 的行动步数时,可以预处理每个元素走出所在块后到达的点及所需步数。
int n, m;
int a[N];
int Bn;
int belong[N];
structqry {int l, r, id;
bool operator < (const qry &rhs) const {
if (belong[l] ^ belong[rhs.l]) return l < rhs.l;
return belong[l] & 1 ? r < rhs.r : r > rhs.r;
}
} q[M];
int ans[M];
intmain() {
for (int i = 1; i <= m; i ++)
std::cin >> q[i].l >> q[i].r, q[i].id = i;
for (int i = 1; i <= n; i ++) belong[i] = (i - 1) / Bn + 1;
std::sort(q + 1, q + 1 + m);
int l = 1, r = 0;
for (int i = 1; i <= m; i ++) {
while (r < q[i].r) add(a[++ r]);
while (l > q[i].l) add(a[-- l]);
while (r > q[i].r) dec(a[r --]);
while (l < q[i].l) dec(a[l ++]);
// ans[q[i].id] = ...
}
}
高维莫队
对于 k 维莫队,对前 k−1 维分块,第 i∈[1,k−1] 关键字为第 i 维坐标所在块编号,第 k 关键字为第 k 维坐标。
分块大小 O(nm−1k),时间复杂度 O(nm1−1k)。
带修莫队
普通莫队是不支持修改的。
可以给每个询问附加一个时间戳 t,来表示回答该次询问时已经经过了多少次修改。转化为三维莫队。
当 n,m 同阶时,分块大小 O(n23),时间复杂度 O(n53)。
当 n,m 不同阶时,分块大小 O(nm−13),时间复杂度 O(nm23)。
int n, m;
int a[N];
int Bn;
int belong[N];
int mc;
structcg {int x, v;
} c[M];
int mq;
structqry {int l, r, t, id;
bool operator < (const qry &rhs) const {
if (belong[l] ^ belong[rhs.l]) return l < rhs.l;
if (belong[r] ^ belong[rhs.r]) return r < rhs.r;
return t < rhs.t;
}
} q[M];
int ans[M];
intmain() {
for (int i = 1; i <= m; i ++) {
if (opt[0] == 'C') {
mc ++;
std::cin >> c[mc].x >> c[mc].v;
} else {
mq ++;
std::cin >> q[mq].l >> q[mq].r, q[mq].t = mc, q[mq].id = mq;
}
}
for (int i = 1; i <= n; i ++) belong[i] = (i - 1) / Bn + 1;
std::sort(q + 1, q + 1 + mq);
int l = 1, r = 0, t = 0;
for (int i = 1; i <= mq; i ++) {
while (r < q[i].r) add(a[++ r]);
while (l > q[i].l) add(a[-- l]);
while (r > q[i].r) dec(a[r --]);
while (l < q[i].l) dec(a[l ++]);
while (t < q[i].t) {
t ++;
if (q[i].l <= c[t].x && c[t].x <= q[i].r) dec(a[c[t].x]), add(c[t].v);
std::swap(a[c[t].x], c[t].v);
}
while (t > q[i].t) {
if (q[i].l <= c[t].x && c[t].x <= q[i].r) dec(a[c[t].x]), add(c[t].v);
std::swap(a[c[t].x], c[t].v);
t --;
}
// ans[q[i].id] = ...
}
}
回滚莫队
用于解决一类插入容易、删除不易的莫队问题。
当操作方便撤销时,可以考虑直接撤销。或是用栈记录操作,沿着栈撤销。
当操作不方便撤销时,可以考虑加入这些数对答案的影响。
int n, m;
int a[N];
int Bn;
int belong[N];
structqry {int l, r, id;
bool operator < (const qry &rhs) const {
if (belong[l] ^ belong[rhs.l]) return l < rhs.l;
return r < rhs.r;
}
} q[M];
int ans[M];
voidadd(int x, bool rem) {
// ...
}
intmain() {
for (int i = 1; i <= m; i ++)
std::cin >> q[i].l >> q[i].r, q[i].id = i;
for (int i = 1; i <= n; i ++) belong[i] = (i - 1) / Bn + 1;
std::sort(q + 1, q + 1 + m);
for (int x = 1, y = 1; x <= m; x = y = y + 1) {
while (y < m && belong[q[x].l] == belong[q[y + 1].l]) y ++;
int rightpos = belong[q[x].l] * Bn;
// init ...int l = rightpos + 1, r = rightpos;
for (int i = x; i <= y; i ++) {
if (q[i].r <= rightpos) {
for (int j = q[i].l; j <= q[i].r; j ++) add(j, 1);
// ans[q[i].id] = ...// Return to the last state.
} else {
while (r < q[i].r) add(++ r, 0);
// Record the current state.while (l > q[i].l) add(-- l, 1);
// ans[q[i].id] = ...// Return to the last state.
}
}
}
}
若 LCA(x,y)=x,则从 x 到 y 的路径上的点为欧拉序 [Inx,Iny] 中只出现过一次的点。
若 LCA(x,y)≠x,则从 x 到 y 的路径上的点为欧拉序 [Outx,Iny] 中只出现过一次的点与 LCA(x,y)。
int n, m;
int a[N];
int tot, head[N], ver[N * 2], Next[N * 2];
voidadd_edge(int u, int v) {
ver[++ tot] = v; Next[tot] = head[u]; head[u] = tot;
}
int dep[N];
int anc[logN + 1][N];
int In[N], Out[N];
int eul_len, eul[N * 2];
int Bn;
int belong[N * 2];
voiddfs(int u, int fu) {
dep[u] = dep[fu] + 1;
anc[0][u] = fu;
for (int i = 1; i <= logN; i ++) anc[i][u] = anc[i - 1][anc[i - 1][u]];
eul[++ eul_len] = u, In[u] = eul_len;
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == fu) continue;
dfs(v, u);
}
eul[++ eul_len] = u, Out[u] = eul_len;
}
intlca(int x, int y) {
if (dep[x] > dep[y]) std::swap(x, y);
for (int i = logN; i >= 0; i --)
if (dep[x] <= dep[y] - (1 << i)) y = anc[i][y];
if (x == y) return x;
for (int i = logN; i >= 0; i --)
if (anc[i][x] != anc[i][y]) x = anc[i][x], y = anc[i][y];
return anc[0][x];
}
structask {int l, r, z, id;
bool operator < (const qry &rhs) const {
if (belong[l] ^ belong[rhs.l]) return l < rhs.l;
return belong[l] & 1 ? r < rhs.r : r > rhs.r;
}
} q[M];
int ans[M];
bool state[N];
voidattend(int p) {
state[p] ^= 1;
state[p] ? add(a[p]) : dec(a[p]);
}
intmain() {
dfs(1, 0);
for (int i = 1, x, y, z; i <= m; i ++) {
std::cin >> x >> y, z = lca(x, y);
if (In[x] > In[y]) std::swap(x, y);
if (z == x)
q[i].l = In[x], q[i].r = In[y], q[i].z = 0, q[i].id = i;
else
q[i].l = Out[x], q[i].r = In[y], q[i].z = z, q[i].id = i;
}
for (int i = 1; i <= eul_len; i ++) belong[i] = (i - 1) / Bn + 1;
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i ++) {
while (r < q[i].r) attend(eul[++ r]);
while (l > q[i].l) attend(eul[-- l]);
while (r > q[i].r) attend(eul[r --]);
while (l < q[i].l) attend(eul[l ++]);
if (q[i].z)
// Calculate ans[q[i].id] with q[i].zelse// Calculate ans[q[i].id]
}
}
有根树树哈希:设 fu 表示以 u 为根的子树的哈希值,则 fu 即为 u 的所有儿子 v 为根的子树的哈希值构成的多重集的哈希值。考虑使用集合哈希。
fu=⎛⎝c+∑v∈son(u)shift(fv)⎞⎠modm
typedefunsignedlonglong u64;
u64 mask = std::mt19937_64((unsigned)time(0))();
u64 shift(u64 x) {
x ^= mask;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x ^= mask;
return x;
}
u64 f[N];
voiddfs_hash(int u, int fu) {
f[u] = 1;
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == fu) continue;
dfs_hash(v, u);
f[u] += shift(f[v]);
}
}
int tot, head[N], ver[N * 2], Next[N * 2];
voidadd_edge(int u, int v) {
ver[++ tot] = v; Next[tot] = head[u]; head[u] = tot;
}
int dep[N], sze[N], Fa[N], son[N];
voiddfs1(int u) {
dep[u] = dep[Fa[u]] + 1;
sze[u] = 1;
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == Fa[u]) continue;
Fa[v] = u;
dfs1(v);
sze[u] += sze[v];
if (sze[v] > sze[son[u]]) son[u] = v;
}
}
int dfsClock, dfn[N], idx[N];
int chain_top[N];
voiddfs2(int u, int P) {
dfsClock ++;
dfn[u] = dfsClock, idx[dfsClock] = u;
chain_top[u] = P;
if (son[u]) dfs2(son[u], P);
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == Fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
voidchain_operate(int x, int y) {
while (chain_top[x] ^ chain_top[y]) {
if (dep[chain_top[x]] > dep[chain_top[y]]) std::swap(x, y);
// Operate on the [dfn[chain_top[y]], dfn[y]].
y = Fa[chain_top[y]];
}
if (dep[x] > dep[y]) std::swap(x, y);
// Operate on the [dfn[x], dfn[y]].
}
长链剖分
所有长链的长度和为 n。
任意一点到根路径上的所有边,可分成 O(√n) 个长路径的不交并。
int tot, head[N], ver[N * 2], Next[N * 2];
voidadd_edge(int u, int v) {
ver[++ tot] = v; Next[tot] = head[u]; head[u] = tot;
}
int dep[N], max_dep[N], Fa[N], son[N];
voiddfs1(int u) {
max_dep[u] = dep[u] = dep[Fa[u]] + 1;
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == Fa[u]) continue;
Fa[v] = u;
dfs1(v);
if (max_dep[v] > max_dep[u]) max_dep[u] = max_dep[v], son[u] = v;
}
}
int chain_top[N];
voiddfs2(int u, int P) {
chain_top[u] = P;
if (son[u]) dfs2(son[u], P);
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == Fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
记剩余级数为 k′=k−2h。由于当前 x 所在长链长度 len≥2h≥k−2h=k′,可以先让 x 跳到 x 所在长链顶端,若剩余级数为正,则利用预处理的数组向上跳求出答案;若剩余级数为负,则利用预处理的数组向下跳求出答案。
int tot, head[N], ver[N * 2], Next[N * 2];
voidadd_edge(int u, int v) {
ver[++ tot] = v; Next[tot] = head[u]; head[u] = tot;
}
int dep[N], max_dep[N], Fa[N], son[N];
int anc[logN + 1][N];
voiddfs1(int u) {
max_dep[u] = dep[u] = dep[Fa[u]] + 1;
anc[0][u] = Fa[u];
for (int i = 1; i <= logN; i ++) anc[i][u] = anc[i - 1][anc[i - 1][u]];
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == Fa[u]) continue;
Fa[v] = u;
dfs1(v);
if (max_dep[v] > max_dep[u]) max_dep[u] = max_dep[v], son[u] = v;
}
}
int chain_top[N];
std::vector<int> up[N], dn[N];
voiddfs2(int u, int P) {
chain_top[u] = P;
if (u == P) {
int len = max_dep[u] - dep[u] + 1;
for (int step = len, x = u; step; step --, x = Fa[x]) up[u].push_back(x);
for (int step = len, x = u; step; step --, x = son[x]) dn[u].push_back(x);
}
if (son[u]) dfs2(son[u], P);
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == Fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int logx[N];
intfind(int x, int k) {
if (!k) return x;
int h = logx[k];
k -= (1 << h), x = f[x][h];
k -= dep[x] - dep[chain_top[x]], x = chain_top[x];
return k >= 0 ? up[x][k] : dn[x][-k];
}
intmain() {
logx[0] = -1;
for (int i = 1; i <= n; i ++) logx[i] = logx[i >> 1] + 1;
// ...
}
int tot, head[N], ver[N * 2], Next[N * 2];
voidadd_edge(int u, int v) {
ver[++ tot] = v; Next[tot] = head[u]; head[u] = tot;
}
int dfsClock, dfn[N], idx[N];
int sze[N], son[N];
voiddfs(int u, int fu) {
dfsClock ++;
dfn[u] = dfsClock, idx[dfsClock] = u;
sze[u] = 1;
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == fu) continue;
dfs(v, u);
sze[u] += sze[v];
if (sze[v] > sze[son[u]]) son[u] = v;
}
}
voidsolve(int u, int fu, bool rem) {
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == fu || v == son[u]) continue;
solve(v, u, 0);
}
if (son[u]) solve(son[u], u, 1);
for (int i = head[u]; i; i = Next[i]) {
int v = ver[i];
if (v == fu || v == son[u]) continue;
int nl = dfn[v], nr = dfn[v] + sze[v] - 1;
for (int ni = nl; ni <= nr; ni ++) {
int x = idx[ni];
add(x);
}
}
add(u);
// ans[u] = ...if (!rem) {
int nl = dfn[u], nr = dfn[u] + sze[u] - 1;
for (int ni = nl; ni <= nr; ni ++) {
int x = idx[ni];
dec(x);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)