路径上若干条树的包含
题意
别人今天期中考,而你依然在机房里为今年的 NOIP 努力刷题。
一道两道三四道,紫题黑题不会题。
暴力枚举TLE,数组开小爆零寄……
已过立冬,窗外寒风瑟瑟,但是你看到有一棵树屹立不倒,你也想成为像大树一般挺拔的人。
一、二、三……你仔细数着,发现树上有
忽然你手里多出了一个东西,鼓鼓的,原来是一个大小为
“你好啊,OIer!”那棵树朝你说道,“我们来玩个游戏吧。”你当然答应下来。
“游戏有
“每次我可以向你手上的集合里塞入新的一条路径……”这就是它的魔力吗?
“或者我给你一条路径,你要告诉我它完全包含了集合中多少条路径,怎么样?”你爽快答应下来了,真是一个好玩的游戏呢。思索片刻,你发现这竟然和你最近做的题目有些相像……
形式化题意:
一棵
- 查询路径
完全包含 中多少条路径; - 向
中插入一条路径; - 向
中删除一条路径。
题目分析
考虑
- 若
,即 为折链。
发现 需要在 的子树里, 在 的子树里。可以用 dfn 判断子树包含关系,即 。 - 若
,即 为直链。
那么需要 的其中一端在 的子树里。对于另一端的限制,我们首先找到 的一个孩子 ,满足 ,那么另一端需要在除了 的结点中。我们可以画图帮助理解:
的一端在蓝色部分中,另一端在绿色部分中。我们钦定了 ,所以这里可以分成不重复的两部分: 或 。
于是,我们发现一条
动态往平面里插入带权矩形,查询包含某一个点的矩形权值和。
对于静态问题,直接扫描线。此题使用树套树,或者 CDQ 分治。可不要小瞧树套树!要是强制在线,只能老老实实做数据结构喽。
代码
树套树
#pragma GCC optimize("Ofast", "inline", "omit-frame-pointer", "no-stack-protector", "fast-math", "unroll-loops") #include <cstdio> #include <iostream> #include <vector> using namespace std; char ST; const int MAX = 1 << 26; char buf[MAX], *ip = buf, obuf[MAX], *op = obuf; #define putchar(x) *::op++ = x template <typename T> inline void read(T &x) { x = 0; char ch = *ip++; for (; ch < 48; ch = *ip++); for (; ch >= 48; ch = *ip++) x = (x << 3) + (x << 1) + (ch ^ 48); } template <typename T> inline void write(T x) { static short stack[20], top(0); do stack[++top] = x % 10; while (x /= 10); while (top) putchar(stack[top--] | 48); } const int N = 100005; const int lgN = __lg(N) + 1; int n, m, q; vector<int> edge[N]; int dpt[N], fa[N], st[lgN][N]; int L[N], R[N], timer; void dfs(int u) { st[0][L[u] = ++timer] = u; for (int v : edge[u]) if (v != fa[u]) dpt[v] = dpt[u] + 1, fa[v] = u, dfs(v); R[u] = timer; } inline int Min(int u, int v) { return dpt[u] < dpt[v] ? u : v; } // when dpt[u] == dpt[v], returns v inline int plca(int u, int v) { // requires L[u] < L[v], u != v int p = __lg((v = L[v]) - (u = L[u])++); return Min(st[p][u], st[p][v - (1 << p) + 1]); } struct treeNode { int ls, rs, tag; // tag always } tree[N * 210]; int nodeCnt, root[N]; void _add(int &idx, int trl, int trr, int l, int r) { if (!idx) idx = ++nodeCnt; if (l <= trl && trr <= r) return ++tree[idx].tag, void(); int mid = (trl + trr) >> 1; if (l <= mid) _add(tree[idx].ls, trl, mid, l, r); if (r > mid) _add(tree[idx].rs, mid + 1, trr, l, r); } void _sub(int &idx, int trl, int trr, int l, int r) { if (!idx) idx = ++nodeCnt; if (l <= trl && trr <= r) return --tree[idx].tag, void(); int mid = (trl + trr) >> 1; if (l <= mid) _sub(tree[idx].ls, trl, mid, l, r); if (r > mid) _sub(tree[idx].rs, mid + 1, trr, l, r); } int _query(int idx, int trl, int trr, int p) { if (trl == trr) return tree[idx].tag; int mid = (trl + trr) >> 1; if (p <= mid) return tree[idx].tag + (tree[idx].ls ? _query(tree[idx].ls, trl, mid, p) : 0); return tree[idx].tag + (tree[idx].rs ? _query(tree[idx].rs, mid + 1, trr, p) : 0); } inline void _add(int p, int l, int r) { _add(root[p], 1, n, l, r); } inline void _sub(int p, int l, int r) { _sub(root[p], 1, n, l, r); } inline int _query(int p, int x) { return _query(root[p], 1, n, x); } inline void add(int p, int l, int r) { for (; p <= n; p += p & -p) _add(p, l, r); } inline void sub(int p, int l, int r) { for (; p <= n; p += p & -p) _sub(p, l, r); } inline void add(int L, int R, int l, int r) { add(L, l, r), sub(R + 1, l, r); } inline void sub(int L, int R, int l, int r) { sub(L, l, r), add(R + 1, l, r); } inline int query(int p, int x) { int res = 0; for (; p; p -= p & -p) res += _query(p, x); return res; } inline void modify(int Lu, int Ru, int Lv, int Rv) { if (Lu > Ru || Lv > Rv) return; add(Lv, Rv, Lu, Ru); } inline void modify(int u, int v) { if (L[u] > L[v]) u ^= v ^= u ^= v; int fp = plca(u, v), p = fa[fp]; if (u == p) { // straight path modify(1, L[fp] - 1, L[v], R[v]); modify(L[v], R[v], R[fp] + 1, n); } else { // common path modify(L[u], R[u], L[v], R[v]); } } inline int pathQuery(int u, int v) { if (L[u] > L[v]) u ^= v ^= u ^= v; return query(L[v], L[u]); } char ED; signed main() { #ifdef XuYueming fprintf(stderr, "Memory: %.2lf MB\n", (&ED - &ST) / 1024.0 / 1024.0); #endif #ifndef XuYueming freopen("revolt.in", "r", stdin); freopen("revolt.out", "w", stdout); #endif fread(buf, 1, MAX, stdin), read(n), read(m), read(q); for (int i = 1, u, v; i < n; ++i) { read(u), read(v); edge[u].emplace_back(v); edge[v].emplace_back(u); } dfs(1); for (int k = 1; k < lgN; ++k) for (int i = 1; i + (1 << k) - 1 <= n; ++i) st[k][i] = Min(st[k - 1][i], st[k - 1][i + (1 << (k - 1))]); for (int u, v; m--; ) read(u), read(v), modify(u, v); for (int op, u, v; q--; ) { read(op), read(u), read(v); if (op == 1) write(pathQuery(u, v)), putchar('\n'); else modify(u, v); } fwrite(obuf, 1, op - obuf, stdout); return 0; }
CDQ 分治
#pragma GCC optimize("Ofast", "inline", "omit-frame-pointer", "no-stack-protector", "fast-math", "unroll-loops") #include <cstdio> #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAX = 1 << 26; char buf[MAX], *ip = buf, obuf[MAX], *op = obuf; #define putchar(x) *op++ = x template <typename T> inline void read(T &x) { x = 0; char ch = *ip++; for (; ch < 48; ch = *ip++); for (; ch >= 48; ch = *ip++) x = (x << 3) + (x << 1) + (ch ^ 48); } template <typename T> inline void write(T x) { static short stack[20], top(0); do stack[++top] = x % 10; while (x /= 10); while (top) putchar(stack[top--] | 48); } const int N = 100005; const int lgN = __lg(N) + 1; int n, m, q; vector<int> edge[N]; int dpt[N], fa[N], st[lgN][N]; int L[N], R[N], timer; void dfs(int u) { st[0][L[u] = ++timer] = u; for (int v : edge[u]) if (v != fa[u]) dpt[v] = dpt[u] + 1, fa[v] = u, dfs(v); R[u] = timer; } inline int Min(int u, int v) { return dpt[u] < dpt[v] ? u : v; } // when dpt[u] == dpt[v], returns v inline int plca(int u, int v) { // requires L[u] < L[v], u != v int p = __lg((v = L[v]) - (u = L[u])++); return Min(st[p][u], st[p][v - (1 << p) + 1]); } struct Line { int y, lx, rx, v; } line[N << 3]; int lineCnt, qryCnt, ans[N]; inline void insert(int Lu, int Ru, int Lv, int Rv) { if (Lu > Ru || Lv > Rv) return; line[++lineCnt] = { Lv, Lu, Ru, 1 }; line[++lineCnt] = { Rv + 1, Lu, Ru, -1 }; } inline void pathInsert(int u, int v) { if (L[u] > L[v]) u ^= v ^= u ^= v; int fp = plca(u, v), p = fa[fp]; if (u == p) { // straight path insert(1, L[fp] - 1, L[v], R[v]); insert(L[v], R[v], R[fp] + 1, n); } else { // common path insert(L[u], R[u], L[v], R[v]); } } inline void qryInsert(int idx, int u, int v) { if (L[u] > L[v]) u ^= v ^= u ^= v; line[++lineCnt] = { L[v], L[u], idx, 0 }; } int tree[N]; inline void modify(int p, int v) { for (; p <= n; p += p & -p) tree[p] += v; } inline int query(int p) { int res = 0; for (; p; p &= p - 1) res += tree[p]; return res; } inline void modify(int l, int r, int v) { modify(l, v), modify(r + 1, -v); } void solve(int l, int r) { if (l == r) return; int mid = (l + r) >> 1; solve(l, mid), solve(mid + 1, r); int p = l - 1; for (int q = mid + 1; q <= r; ++q) { while (p + 1 <= mid && line[p + 1].y <= line[q].y) if (line[++p].v) modify(line[p].lx, line[p].rx, line[p].v); if (line[q].v == 0) ans[line[q].rx] += query(line[q].lx); } for (; p >= l; --p) if (line[p].v) modify(line[p].lx, line[p].rx, -line[p].v); inplace_merge(line + l, line + mid + 1, line + r + 1, [] (const Line& a, const Line& b) -> bool { return a.y < b.y; } ); } // divide solved time order // inplace_merge solved Y order // data structure solved X order signed main() { #ifndef XuYueming freopen("revolt.in", "r", stdin); freopen("revolt.out", "w", stdout); #endif fread(buf, 1, MAX, stdin), read(n), read(m), read(q); for (int i = 1, u, v; i < n; ++i) { read(u), read(v); edge[u].emplace_back(v); edge[v].emplace_back(u); } dfs(1); for (int k = 1; k < lgN; ++k) for (int i = 1; i + (1 << k) - 1 <= n; ++i) st[k][i] = Min(st[k - 1][i], st[k - 1][i + (1 << (k - 1))]); for (int u, v; m--; ) read(u), read(v), pathInsert(u, v); for (int op, u, v; q--; ) { read(op), read(u), read(v); if (op == 1) qryInsert(++qryCnt, u, v); else pathInsert(u, v); } solve(1, lineCnt); for (int i = 1; i <= qryCnt; ++i) write(ans[i]), putchar('\n'); fwrite(obuf, 1, op - obuf, stdout); return 0; }
后记
快去 A 了这道题吧,祝你成功!
#include <cstdio> #include <iostream> #include <vector> #include <algorithm> using namespace std; const int MAX = 1 << 26; char buf[MAX], *ip = buf, obuf[MAX], *op = obuf; #define putchar(x) *::op++ = x template <typename T> inline void read(T &x) { x = 0; char ch = *ip++; for (; ch < 48; ch = *ip++); for (; ch >= 48; ch = *ip++) x = (x << 3) + (x << 1) + (ch ^ 48); } template <typename T> inline void write(T x) { static short stack[20], top(0); do stack[++top] = x % 10; while (x /= 10); while (top) putchar(stack[top--] | 48); } const int N = 50010; const int lgN = __lg(N) + 1; int n, m, q; vector<int> edge[N]; int dpt[N], fa[N], st[lgN][N]; int L[N], R[N], timer; void dfs(int u) { st[0][L[u] = ++timer] = u; for (int v : edge[u]) if (v != fa[u]) dpt[v] = dpt[u] + 1, fa[v] = u, dfs(v); R[u] = timer; } inline int Min(int u, int v) { return dpt[u] < dpt[v] ? u : v; } // when dpt[u] == dpt[v], returns v inline int plca(int u, int v) { // requires L[u] < L[v], u != v int p = __lg((v = L[v]) - (u = L[u])++); return Min(st[p][u], st[p][v - (1 << p) + 1]); } namespace TREE { int tree[N], tag[N], timer = -1; inline void clear() { ++timer; } inline void modify(int p, int f) { for (; p <= n; p += p & -p) if (tag[p] != timer) tag[p] = timer, tree[p] = f; else tree[p] += f; } inline void modify(int l, int r, int v) { modify(l, v), modify(r + 1, -v); } inline int query(int p) { int res = 0; for (; p; p &= p - 1) if (tag[p] == timer) res += tree[p]; return res; } } struct Line { int y, l, r, f, idx; } line[N * 5], tq[N * 5]; int lcnt; int VVV[N], UUU; int ans[N]; inline void modify(int Lu, int Ru, int Lv, int Rv, int w) { if (Lu > Ru || Lv > Rv) return; line[++lcnt] = { Lv, Lu, Ru, 1, -w }; line[++lcnt] = { Rv + 1, Lu, Ru, -1, -w }; } void solve(int l, int r, int ql, int qr) { if (ql > qr) return; if (l == r) { for (int i = ql; i <= qr; ++i) if (line[i].idx > 0) ans[line[i].idx] = VVV[l]; return; } int mid = (l + r) >> 1; int L = ql - 1, R = qr + 1; TREE::clear(); for (int i = ql; i <= qr; ++i) { if (line[i].idx > 0) { int v = TREE::query(line[i].l); if (v >= line[i].f) tq[++L] = move(line[i]); else line[i].f -= v, tq[--R] = move(line[i]); } else { if (-line[i].idx <= VVV[mid]) TREE::modify(line[i].l, line[i].r, line[i].f), tq[++L] = move(line[i]); else tq[--R] = move(line[i]); } } reverse(tq + R, tq + qr + 1); for (int i = ql; i <= qr; ++i) line[i] = move(tq[i]); solve(mid + 1, r, R, qr); solve(l, mid, ql, L); } signed main() { fread(buf, 1, MAX, stdin), read(n), read(m), read(q); for (int i = 1, u, v; i < n; ++i) { read(u), read(v); edge[u].emplace_back(v); edge[v].emplace_back(u); } dfs(1); for (int k = 1; k < lgN; ++k) for (int i = 1; i + (1 << k) - 1 <= n; ++i) st[k][i] = Min(st[k - 1][i], st[k - 1][i + (1 << (k - 1))]); for (int i = 1, u, v, w; i <= m; ++i) { read(u), read(v), read(w); if (L[u] > L[v]) u ^= v ^= u ^= v; int fp = plca(u, v), p = fa[fp]; if (u == p) { // straight path modify(1, L[fp] - 1, L[v], R[v], w); modify(L[v], R[v], R[fp] + 1, n, w); } else { // common path modify(L[u], R[u], L[v], R[v], w); } VVV[++UUU] = w; } for (int i = 1, u, v, k; i <= q; ++i) { read(u), read(v), read(k); line[++lcnt] = { L[v], L[u], 0, k, i }; ans[i] = -1; } sort(line + 1, line + lcnt + 1, [] (const Line& a, const Line& b) -> bool { if (a.y == b.y) return a.idx < b.idx; // modify first return a.y < b.y; } ); sort(VVV + 1, VVV + UUU + 1); UUU = unique(VVV + 1, VVV + UUU + 1) - VVV - 1; solve(1, UUU, 1, lcnt); for (int i = 1; i <= q; ++i) write(ans[i]), putchar('\n'); fwrite(obuf, 1, op - obuf, stdout); return 0; }
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18535929。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!