洛谷 P4242. 树上的毒瘤

news/2025/11/14 19:02:03/文章来源:https://www.cnblogs.com/Conan15-blog/p/19223125

题目描述。

很不错的题。Tag:虚树、树链剖分、换根相关。
本文不讲解上述前置知识。

看到树上颜色段覆盖、查询,容易联想到树链剖分
树上颜色段数量是不难统计的。先用树链剖分拍在序列上,然后区间只要维护颜色段数、左右颜色即可。

看到每次询问大小为 \(m\) 的子集,并不难联想到虚树相关。对于每次询问建立点集 \(S\) 的虚树。
钦定 \(1\) 号点为根,通过树链剖分 \(O(m \log n)\) 加和求出 \(W_1\)\(1\) 号点的答案。
但是题目要求点集 \(S\) 中每个点的答案?这启发我们做换根
具体地,记录一下虚树上每个子树有多少点在点集 \(S\) 内,然后换根其实和求“每个点到 \(u\) 的路径长度之和”是一样的,只是路径长度改成了路上的颜色段数。

变量重名挂得早,封装就是好!
代码中含有少量的魔怔,可自行去除注释。

  • 在建虚树的时候,需要查询父亲到当前节点路径上的颜色段数和两端点颜色,这里注意不要合并反了,因为合并颜色段是不满足交换律的。
#include <bits/stdc++.h>
//#include <windows.h>
using namespace std;
const int N = 1e5 + 5, M = N << 1;
int n, q, a[N];
int h[N], e[M], ne[M], idx;
inline void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; }struct Path { int a, b, c; } ;	// ×óÓÒÑÕÉ«¡¢ÑÕÉ«¶ÎÊý 
Path merge(Path x, Path y) { return (Path){x.a, y.b, x.c + y.c - (x.b == y.a)}; }void change(int u, int l, int r, int d);
Path query(int u, int l, int r);//----------------------------------------------------------------------------int dep[N], fa[N], sz[N], son[N];
void dfs(int u, int father) {fa[u] = father, dep[u] = dep[father] + 1, sz[u] = 1;for (int i = h[u]; ~i; i = ne[i]) {int v = e[i]; if (v == father) continue;dfs(v, u), sz[u] += sz[v];if (!son[u] || sz[son[u]] < sz[v]) son[u] = v;}
}
int top[N], dfn[N], id[N], tot;
void dfs2(int u, int t) {top[u] = t, dfn[u] = ++tot, id[tot] = u;if (son[u]) dfs2(son[u], t);for (int i = h[u]; ~i; i = ne[i]) {int v = e[i]; if (v == fa[u] || v == son[u]) continue;dfs2(v, v);}
}inline int lca(int a, int b) {while (top[a] ^ top[b]) {if (dep[top[a]] < dep[top[b]]) swap(a, b);a = fa[top[a]];}if (dep[a] > dep[b]) swap(a, b);return a;
}void change_path(int u, int v, int d) {while (top[u] ^ top[v]) {if (dep[top[u]] < dep[top[v]]) swap(u, v);
//		cout << "Change: "; for (int i = dfn[top[u]]; i <= dfn[u]; i++) cout << id[i] << ' '; puts("");change(1, dfn[top[u]], dfn[u], d), u = fa[top[u]];}
//	cout << "Change: "; for (int i = dfn[u]; i <= dfn[v]; i++) cout << id[i] << ' '; puts("");if (dep[u] > dep[v]) swap(u, v);change(1, dfn[u], dfn[v], d);
}
Path query_path(int u, int v) {	// ÇÕ¶¨ u ÊÇ v µÄ׿ÏÈ Path res = (Path){-1, -1, -1};while (top[u] ^ top[v]) {Path val = query(1, dfn[top[v]], dfn[v]);if (res.a == -1) res = val;else res = merge(val, res);v = fa[top[v]];}Path val = query(1, dfn[u], dfn[v]);if (res.a == -1) res = val;else res = merge(val, res);return res;
}
//----------------------------------------------------------------------------struct Tree {int l, r, cov;Path res;
} tr[N << 2];
inline void pushup(int u) { tr[u].res = merge(tr[u << 1].res, tr[u << 1 | 1].res); }
inline void update(int u, int d) { tr[u].cov = d, tr[u].res = (Path){d, d, 1}; }
inline void pushdown(int u) { if (tr[u].cov) update(u << 1, tr[u].cov), update(u << 1 | 1, tr[u].cov), tr[u].cov = 0; }
void build(int u, int l, int r) {tr[u].l = l, tr[u].r = r, tr[u].cov = 0;if (l == r) return tr[u].res = (Path){a[id[l]], a[id[l]], 1}, void();int mid = l + r >> 1;build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);pushup(u);
}
void change(int u, int l, int r, int d) {if (tr[u].l >= l && tr[u].r <= r) return update(u, d), void();pushdown(u);int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) change(u << 1, l, r, d);if (r > mid) change(u << 1 | 1, l, r, d);pushup(u);
}
Path query(int u, int l, int r) {if (tr[u].l >= l && tr[u].r <= r) return tr[u].res;pushdown(u);int mid = tr[u].l + tr[u].r >> 1;if (r <= mid) return query(u << 1, l, r);if (l > mid) return query(u << 1 | 1, l, r);return merge(query(u << 1, l, r), query(u << 1 | 1, l, r));
}int qwq[N];
void print(int u) {if (tr[u].l == tr[u].r) return qwq[id[tr[u].l]] = tr[u].res.a, void();pushdown(u);print(u << 1), print(u << 1 | 1);
}//----------------------------------------------------------------------------struct Virtual_Tree {int m, h[N], e[M], ne[M], idx;Path w[M];	// fa -> soninline void add(int a, int b, Path c) { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++; }int cnt[N];long long Ans[N], ans;void dfs1(int u) { for (int i = h[u]; ~i; i = ne[i]) dfs1(e[i]), cnt[u] += cnt[e[i]]; }void dfs2(int u) {Ans[u] = ans;
//		cout << "Ans: " << u << ' ' << ans << endl;long long pre = ans;for (int i = h[u]; ~i; i = ne[i]) {int v = e[i];int in = cnt[v], out = m - in, a = w[i].a, b = w[i].b, c = w[i].c;ans -= in * 1ll * (c - 1), ans += out * 1ll * (c - 1);dfs2(v), ans = pre;}}void clr(int u) {cnt[u] = Ans[u] = 0;for (int i = h[u]; ~i; i = ne[i]) clr(e[i]);h[u] = -1;}
} VT;int arr[N], qry[N], m;
int stk[N], tt;
inline bool cmp(int a, int b) { return dfn[a] < dfn[b]; }
inline void insert(int u, int v) {	// ÇÕ¶¨ u ÊÇ v µÄ׿ÏÈ Path w = query_path(u, v);
//	cout << "Virtual Tree Add edge: " << u << ' ' << v << '\t' << w.a << ' ' << w.b << ' ' << w.c << endl;VT.add(u, v, w);
}
void build_VTree() {VT.ans = 0ll, VT.idx = 0;sort(arr + 1, arr + 1 + m, cmp);for (int i = 1; i <= m; i++) VT.cnt[arr[i]] = 1, VT.ans += query_path(1, arr[i]).c/*, cout << "Init: " << arr[i] << ' ' << query_path(1, arr[i]).c << endl*/;stk[tt = 1] = 1;for (int i = 1; i <= m; i++) {if (arr[i] == 1) continue;int u = arr[i], l = lca(arr[i], stk[tt]);while (dfn[l] < dfn[stk[tt - 1]]) insert(stk[tt - 1], stk[tt]), tt--;if (l == stk[tt]) stk[++tt] = u;else if (dfn[stk[tt - 1]] < dfn[l] && dfn[l] < dfn[stk[tt]]) insert(l, stk[tt]), tt--, stk[++tt] = l, stk[++tt] = u;else if (l == stk[tt - 1]) insert(stk[tt - 1], stk[tt]), tt--, stk[++tt] = u;}while (tt > 1) insert(stk[tt - 1], stk[tt]), tt--;
}int main() {
//	freopen("rubbish.out", "w", stdout); scanf("%d%d", &n, &q);for (int i = 1; i <= n; i++) h[i] = VT.h[i] = -1; idx = VT.idx = 0;for (int i = 1; i <= n; i++) scanf("%d", &a[i]);for (int i = 1, a, b; i < n; i++) scanf("%d%d", &a, &b), add(a, b), add(b, a);dfs(1, 0), dfs2(1, 1), build(1, 1, n);//	cout << "Dfn: "; for (int i = 1; i <= n; i++) cout << dfn[i] << ' '; puts("");
//	cout << "Top: "; for (int i = 1; i <= n; i++) cout << top[i] << ' '; puts("");while (q--) {int op; scanf("%d", &op);if (op == 1) {int u, v, d; scanf("%d%d%d", &u, &v, &d);change_path(u, v, d);} else {scanf("%d", &m), VT.m = m;for (int i = 1; i <= m; i++) scanf("%d", &arr[i]), qry[i] = arr[i];build_VTree();VT.dfs1(1), VT.dfs2(1);
//    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);//????for (int i = 1; i <= m; i++) printf("%lld ", VT.Ans[qry[i]]); puts("");
//    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY |FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE);//??????VT.clr(1);}
//		cout << "Print: "; print(1); for (int i = 1; i <= n; i++) printf("%d ", qwq[i]); puts("");}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/965637.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Number Theory

写一些不是很熟识的东西。 约定:一般情况下 \(p\) 是质数。 Theorems Wilsons theorem\((p-1)!\equiv -1 \pmod p\),等价于 \(p\) 是素数。\(\text{proof.}\) 原式等价于方程 \(px+(p-1)!y=-1\),方程显然有解,且若…

2025年11月眉笔选购指南:花西子/植村秀/珂拉琪等5大品牌实测,新手闭眼入款竟是它​

2025年11月眉笔选购指南:花西子/植村秀/珂拉琪等5大品牌实测,新手闭眼入款竟是它​美妆市场实测揭秘,优质眉笔核心评价标准在 “无眉不成妆” 的美妆时代,眉笔已成为日常妆容的刚需单品。一支优质眉笔需兼顾 “上手…

Upcoming Rust language features for kernel development - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

详细介绍:Linux网络性能测试利器:iperf3使用指南

详细介绍:Linux网络性能测试利器:iperf3使用指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

linux 安装telnet 服务

在 Linux 系统中,telnet 服务通常指的是 telnet 工具,而不是完整的 Telnet 服务(如 telnetd)。如果你想要安装 telnet 工具(用于远程登录),可以使用不同的包管理器来安装。 ✅ 一、安装 telnet 工具(用于远程登…

实用指南:【STM32】RTC实时时钟

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

探索乐泰胶水:性能与适用场景全解析

提起“乐泰胶水”,很多人第一反应是“听说过,但具体能做什么?”。作为汉高旗下的工业胶黏剂头部品牌,乐泰的产品矩阵覆盖从家庭DIY到工业制造的全场景需求。但真正让它在专业领域站稳脚跟的,是其针对电机行业、流…

在 CSharp 中调用 Wolfram Language (Mathematica)

安装 Wolfram Engine (https://www.wolfram.com/engine/) 安装完毕后,按提示注册或登录 找到 <Wolfram dir>\SystemFiles\Links\NETLink 目录 在需要使用 Wolfram 的 CSharp 项目中引用 Wolfram.NETLink.dll …

【System Beats!】第七章 链接

链接 相关定义即将各种代码和数据片段收集并组合成为一个单一文件的过程。 主要任务:符号解析与重定位 目标文件:编译器将源代码文件编译后的产物,但还未加载链接为最后的可执行文件。 可执行文件:链接后的最终产物…

oracle 11g r2 linux

你提到的 "Oracle 11g R2 Linux" 是 Oracle 数据库的一个版本,主要用于 Linux 系统。以下是一些与 Oracle 11g R2 在 Linux 上相关的关键信息和命令示例,帮助你了解如何在 Linux 环境中安装、配置和管理 O…

实用指南:接口测试 | 使用Postman实际场景化测试

实用指南:接口测试 | 使用Postman实际场景化测试2025-11-14 18:35 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; displa…

应用程序建立的数据库连接,也就是非交互式连接 是什么时候开始的?什么时候结束?连接结束后 会影响应用程序操作db失败吗? 还有就是如果连接关闭了 会立马重新建立新的连接吗?

问题: 应用程序建立的数据库连接,也就是非交互式连接 是什么时候开始的?什么时候结束?连接结束后 会影响应用程序操作db失败吗? 还有就是如果连接关闭了 会立马重新建立新的连接吗?解答: 我们讨论的是应用程序通…

2025高压合金管实力厂家推荐榜:5310/6479 高压合金管型号领衔,天津大无缝联合钢铁有限公司五星领跑工业用材赛道

在工业生产、能源输送等场景中,合金管、高压合金管的品质直接影响设备运行稳定性,5310 高压合金管与 6479 高压合金管更是核心刚需产品。2025 年榜单聚焦产品性能、技术实力与应用适配性,精选 4 家优质企业,为采购…

Kafka协调器:消费者组管理与重平衡机制 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

#题解#洛谷P1884#二维离散化#

传送门 分析x,y的范围-1e8~1e8,需要离散化。定义f[i][j]是左上角(ci,cj)右下角(c(i+1),c(j+1))染色情况代码实现 #include<bits/stdc++.h> using namespace std; #define MAXN 4010 int n, btop, ctop; int…

HarmonyOS应用配置文件与资源组织深度解析 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025扫描电镜精选榜:富泰微五星领衔,日立、国仪携超高分辨率/钨灯丝 SEM,适配科研工业多元需求

随着纳米科技与材料科学的发展,扫描电镜(SEM)已成为微观表征核心设备,涵盖进口与国产、钨灯丝与场发射、FIB 与超高分辨率等多元类型。2025 年榜单聚焦技术实力与用户口碑,精选 3 家优质企业,为不同场景提供精准…

2025济南单招综评培训机构排行榜:3 家实力学校口碑出圈 易升教育五星优选 解锁适配不同考生的升学备考靠谱路径

随着单招综评成为多元升学的重要路径,济南单招综评培训市场愈发成熟。本文结合办学资质、师资实力、升学成果及口碑反馈,精选 3 家优质济南单招综评机构,其中济南易升教育学校以全维度优势获评五星推荐,为考生提供…

2025山东公考面试/笔试/考试/辅导培训五星推荐榜:三家优质机构精准适配备考需求,助力高效上岸

2025年公考竞争持续升温,优质的公考培训、公考面试培训、公考笔试培训成为考生高效备考的关键。本次推荐榜聚焦口碑与实效,精选 3 家五星公考辅导机构,为不同基础、不同场景的考生提供靠谱参考。 山东邦荣公考【推荐…