题意

有一棵 \(n\) 个节点且以 \(1\) 为根的树,把它复制成 \(m\) 个版本,有 \(q\) 次操作,每次对 \([l, r]\) 这些版本的 \(v\) 节点到根的路径收缩起来。

收缩 \(v\) 也就是把 \(v\) 到根路径上(除了根)所有点的父亲都变成根。

最后查询每个版本的每个点的 \(dep\) 之和。

数据范围

\(n, m, q \le 2 \times 10^5\)

题解

操作顺序是无所谓的,我们假设操作了点集 \(S\) ,那么最后被缩上去的点其实就是 \(\{S, root\}\) 构成虚树经过的节点。

每个点的深度其实它原来的深度减去它到根(除了根与根的儿子)被缩的点的个数。

考虑祖先对它的贡献是比较麻烦的,我们不妨考虑它对于祖先的贡献,其实就是每个深度 \(\ge 2\) 的节点的子树 \(size\) 之和。

那么我们把操作离线,只需要动态维护虚树经过所有点的权值和。

这其实是一个经典的动态虚树的问题,按照 \(dfs\) 序,用 std :: set 维护当前的点集,假设插入点为 \(k\) 找到它的前驱 \(l\) 与后继 \(r\) ,令 \(\mathrm{LCA}(l, k), \mathrm{LCA}(r, k)\) 深度较大点为 \(f\) ,那么这次新产生的路径是 \((k, f)\) 的路径(注意 \(f\) 原来就是存在于虚树中的,需要去掉),删除是类似的。

注意可能一个点被缩多次,我们需要利用 std :: multiset ,然后每次插入删除的时候查找是否还存在即可。

复杂度是 \(\mathcal O((n + q) \log n + m)\) 的。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl using namespace std; typedef long long ll;
typedef pair<int, int> PII; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("1954.in", "r", stdin);
freopen ("1954.out", "w", stdout);
#endif
} const int N = 2e5 + 1e3; vector<int> G[N]; ll ans = 0, sum[N]; int n, m, q, dep[N], anc[N][20], Log2[N], sz[N], dfn[N]; void Dfs_Init(int u, int fa = 0) {
static int clk = 0; dfn[u] = ++ clk;
dep[u] = dep[anc[u][0] = fa] + 1;
ans += dep[u]; sz[u] = 1;
for (int v : G[u]) if (v != fa) Dfs_Init(v, u), sz[u] += sz[v];
} void Get_Sum(int u, int fa = 0) {
sum[u] = sum[fa] + (dep[u] > 1) * sz[u];
for (int v : G[u]) if (v != fa) Get_Sum(v, u);
} struct Cmp {
inline bool operator () (const int &lhs, const int &rhs) { return dfn[lhs] < dfn[rhs]; }
}; vector<int> add[N], del[N]; multiset<int, Cmp> S; inline int Lca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
int gap = dep[x] - dep[y];
For (i, 0, Log2[gap])
if (gap >> i & 1) x = anc[x][i];
if (x == y) return x;
Fordown (i, Log2[dep[x]], 0)
if (anc[x][i] != anc[y][i]) x = anc[x][i], y = anc[y][i];
return anc[x][0];
} int Find(int x) {
PII res; auto it = S.upper_bound(x);
if (it != S.end()) {
int tmp = Lca(*it, x); chkmax(res, {dep[tmp], tmp});
}
if (it != S.begin()) {
int tmp = Lca(*prev(it), x); chkmax(res, {dep[tmp], tmp});
}
return res.second ? res.second : x;
} int main () { File(); n = read(); m = read(); q = read();
For (i, 1, n - 1) {
int u = read(), v = read();
G[u].push_back(v); G[v].push_back(u);
} while (q --) {
int l = read(), r = read(), v = read();
add[l].push_back(v); del[r + 1].push_back(v);
} dep[0] = -1; Dfs_Init(1); Get_Sum(1); For (i, 2, n) Log2[i] = Log2[i >> 1] + 1;
For (j, 1, Log2[n]) For (i, 1, n)
anc[i][j] = anc[anc[i][j - 1]][j - 1]; S.insert(1);
For (i, 1, m) {
for (int x : add[i]) {
if (S.find(x) == S.end())
ans -= sum[x] - sum[Find(x)];
S.insert(x);
}
for (int x : del[i]) {
S.erase(S.find(x));
if (S.find(x) == S.end())
ans += sum[x] - sum[Find(x)];
}
printf ("%lld%c", ans, i == iend ? '\n' : ' ');
} return 0; }

hihoCoder #1954 : 压缩树(虚树)的更多相关文章

  1. 仙人掌 && 圆方树 && 虚树 总结

    仙人掌 && 圆方树 && 虚树 总结 Part1 仙人掌 定义 仙人掌是满足以下两个限制的图: 图完全联通. 不存在一条边处在两个环中. 其中第二个限制让仙人掌的题做 ...

  2. [SDOI2018]战略游戏(圆方树+虚树)

    喜闻乐见的圆方树+虚树 图上不好做,先建出圆方树. 然后答案就是没被选到的且至少有两条边可以走到被选中的点的圆点的数量. 语文不好,但结论画画图即可得出. 然后套路建出虚树. 发现在虚树上DP可以得出 ...

  3. 51Nod1868 彩色树 虚树

    原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1868.html 题目传送门 - 51Nod1868 题意 给定一颗 $n$个点的树,每个点一个 $[ ...

  4. Codechef Sad Pairs——圆方树+虚树+树上差分

    SADPAIRS 删点不连通,点双,圆方树 非割点:没有影响 割点:子树DP一下 有不同颜色,所以建立虚树 在圆方树上dfs时候 如果当前点是割点 1.统计当前颜色虚树上的不连通点对,树形DP即可 2 ...

  5. BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)

    Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着 ...

  6. Luogu P4606 [SDOI2018] 战略游戏 圆方树 虚树

    https://www.luogu.org/problemnew/show/P4606 把原来的图的点双联通分量缩点(每个双联通分量建一个点,每个割点再建一个点)(用符合逻辑的方式)建一棵树(我最开始 ...

  7. BZOJ.5329.[SDOI2018]战略游戏(圆方树 虚树)

    题目链接 显然先建圆方树,方点权值为0圆点权值为1,两点间的答案就是路径权值和减去起点终点. 对于询问,显然可以建虚树.但是只需要计算两关键点间路径权值,所以不需要建出虚树.统计DFS序相邻的两关键点 ...

  8. UOJ.87.mx的仙人掌(圆方树 虚树)(未AC)

    题目链接 本代码10分(感觉速度还行..). 建圆方树,预处理一些东西.对询问建虚树. 对于虚树上的圆点直接做:对于方点特判,枚举其所有儿子,如果子节点不在该方点代表的环中,跳到那个点并更新其val, ...

  9. 洛谷P4606 [SDOI2018]战略游戏 【圆方树 + 虚树】

    题目链接 洛谷P4606 双倍经验:弱化版 题解 两点之间必经的点就是圆方树上两点之间的圆点 所以只需建出圆方树 每次询问建出虚树,统计一下虚树边上有多少圆点即可 还要讨论一下经不经过根\(1\)的情 ...

随机推荐

  1. 前端知识复习: JS选中变色

    前端知识复习:JS选中变色 上篇文章 :前端知识复习:Html DIV 图文混排(文字放在图片下边) Js选中图片效果 <!DOCTYPE html> <html xmlns=&qu ...

  2. 【转】解决未能加载文件或程序集'WebGrease‘的问题

    在多个视图中,如果有使用共用的样式代码,可以把它们移至CSS文件中去.今天Insus.NET就举例一个例子来说明. 比如前2篇中<ASP.NET MVC图片管理(上传,预览与显示)>htt ...

  3. 简简单单的Vue4(vue-cie@3.x,vue’Debug[调试],vue‘sHttp)

    既然选择了远方,便只顾风雨兼程! __HANS许 系列:零基础搭建前后端分离项目 系列:零基础搭建前后端分离项目 vue-cli@3.x 创建项目 Vue的Debug(调试) Vue的Http请求 提 ...

  4. java_异常

    一.什么是异常? 1.当程序”运行后”,当jvm遇到一些无法处理的情况,例如:整数/0,这就表示jvm遇到一种”异常情况”. 通常jvm能够识别这些异常并在控制台打印异常信息,并结束程序 2.为了解决 ...

  5. nginx系列10:通过upstream模块选择上游服务器和负载均衡策略round-robin

    upstream模块的使用方法 1,使用upstream和server指令来选择上游服务器 这两个指令的语法如下图: 示例: 2,对上游服务使用keepalive长连接 负载均衡策略round-rob ...

  6. Java建造(Builder)模式

    一.什么是建造模式: 建造模式可以将一个产品的内部表象与产品的生成过程分割开来,从而使一个建造过程生成具有不同内部表象的产品.客户端不需要知道产品内部的结构和生产过程. 二.建造模式的结构: Buil ...

  7. sql 服务启动失败 SQL Server(MSSQLSERVER) 错误码126

    SQL配置管理器-->sql server 网络配置-->mssqlerver的协议-->VIA禁用服务

  8. Webpack 4教程 - 第七部分 减少打包体积与Tree Shaking

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者.原文出处:https://wanago.io/2018/08/13/webpack-4-course-part ...

  9. hbase 工作原理

    一.HBASE介绍HBase是一个高可靠性.高性能.面向列.可伸缩的分布式存储系统,利用HBase技术可在廉价PC Server上搭建大规模结构化的存储集群.HBase的目标是存储并处理大型数据,具体 ...

  10. C#之UDP通信

    简介 C#中的udp通信关键类:Udpclient,它位于命名空间System.Net.Sockets中,发送接收都是UdpClient类, 命名空间 using System.Net.Sockets ...