传送门

看到要求两棵树的 \(lca\) 深度不太好操作

考虑枚举第二棵树的 \(lca\),这样剩下的都是只和第一棵树有关的

而注意到 \(dis(x,y)=d(x)+d(y)-2d(lca(x,y))\)

那么 \(d(x)+d(y)-d(lca(x,y))=\frac{1}{2}(dis(x,y)+d(x)+d(y))\)

这样就好多了,可以直接沿用WC2018通道的做法

对于第一棵树进行边分治,把两边的集合合起来在第二棵树上建个虚树

然后在虚树上枚举 \(lca\),\(dp\) 统计不属于同一个集合的答案

复杂度 \(\Theta(nlog^2n)\) 可以强行优化到 \(nlogn\)

上述做法不够优美

考虑另外一种做法,仍然是对于第一棵树进行边分治

对于每个点,维护每一次边分治时的集合和到分治中心的距离

那么只需要在第二棵树上枚举 \(lca\) 统计每次边分治时不同集合的最大的贡献就好了

这个可以用线段树(二叉树)合并实现,\(nlogn\)

然后考虑怎么维护每一次边分治时的集合和到分治中心的距离

直接想法是直接暴力插入,\(nlog^2n\)

然而边分治每次是分成两个集合,类似一个线段树的结构

所以可以对于每个点维护一个线段树(二叉树),要保证树上每个点和是哪一次边分治对应

只需要将这些点的树的当前点记录下来

每次边分治的第一个集合向左跳,第二个向右跳就可以 \(nlogn\) 建树了

复杂度 \(\Theta(nlogn)\)

# include <bits/stdc++.h>
using namespace std;
typedef long long ll; namespace IO {
const int maxn(1 << 21 | 1); char ibuf[maxn], *iS, *iT, c;
int f; inline char Getc() {
return iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, maxn, stdin), (iS == iT ? EOF : *iS++)) : *iS++;
} template <class Int> inline void In(Int &x) {
for (c = Getc(), f = 1; c < '0' || c > '9'; c = Getc()) f = c == '-' ? -1 : 1;
for (x = 0; c >= '0' && c <= '9'; c = Getc()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= f;
}
} using IO :: In; const int maxn(4e5 + 5);
const ll inf(1e18); int first1[maxn << 1], first2[maxn], cnt, head[maxn], m, n, fw[maxn << 1], tmp[maxn << 1], len;
int size[maxn << 1], mn, rte, vis[maxn << 2], sz;
int rt[maxn], tot, ls[maxn * 40], rs[maxn * 40], fa[maxn], fc[maxn];
ll dis[maxn], ans, lmx[maxn * 40], rmx[maxn * 40]; struct Edge {
int to, next, w;
} edg[maxn << 1], edge1[maxn << 2], edge2[maxn << 1]; inline void Addpre(int u, int v, int w) {
edg[cnt] = (Edge){v, head[u], w}, head[u] = cnt++;
edg[cnt] = (Edge){u, head[v], w}, head[v] = cnt++;
} inline void Add1(int u, int v, int w) {
edge1[cnt] = (Edge){v, first1[u], w}, first1[u] = cnt++;
edge1[cnt] = (Edge){u, first1[v], w}, first1[v] = cnt++;
} inline void Add2(int u, int v, int w) {
edge2[cnt] = (Edge){v, first2[u], w}, first2[u] = cnt++;
edge2[cnt] = (Edge){u, first2[v], w}, first2[v] = cnt++;
} int Build(int l, int r) {
if (l > r) return 0;
if (l == r) return tmp[l];
int cur, ls, rs, mid;
cur = ++m, mid = (l + r) >> 1;
ls = Build(l, mid), rs = Build(mid + 1, r);
if (ls) Add1(cur, ls, fw[ls]);
if (rs) Add1(cur, rs, fw[rs]);
return cur;
} void Rebuild(int u, int ff) {
int e, v, ls, rs, mid;
len = 0;
for (e = head[u]; ~e; e = edg[e].next)
if ((v = edg[e].to) != ff) fw[v] = edg[e].w, tmp[++len] = v;
mid = (len + 1) >> 1;
ls = Build(1, mid), rs = Build(mid + 1, len);
if (ls) Add1(u, ls, fw[ls]);
if (rs) Add1(u, rs, fw[rs]);
for (e = head[u]; ~e; e = edg[e].next)
if ((v = edg[e].to) != ff) dis[v] = dis[u] + fw[v], Rebuild(v, u);
} void Getroot(int u, int ff, int fe) {
int e, v;
size[u] = 1;
for (e = first1[u]; ~e; e = edge1[e].next)
if ((v = edge1[e].to) != ff && !vis[e]) {
Getroot(v, u, e);
size[u] += size[v];
}
if (abs(sz - size[u] * 2) < mn) mn = abs(sz - size[u] * 2), rte = fe;
} void Dfs1(int u, int ff, int type, ll d) {
if (u <= n) {
++tot, lmx[tot] = rmx[tot] = -inf;
if (fa[u]) fc[u] ? rs[fa[u]] = tot : ls[fa[u]] = tot;
else rt[u] = tot;
fa[u] = tot, fc[u] = type;
type ? rmx[tot] = d + dis[u] : lmx[tot] = d + dis[u];
}
int e, v;
for (e = first1[u]; ~e; e = edge1[e].next)
if ((v = edge1[e].to) != ff && !vis[e]) Dfs1(v, u, type, d + edge1[e].w);
} void Solve(int nrt) {
if (nrt == -1 || vis[nrt]) return;
int rt1, rt2, tmp1, tmp2;
rt1 = edge1[nrt].to, rt2 = edge1[nrt ^ 1].to;
vis[nrt] = vis[nrt ^ 1] = 1;
tmp1 = size[rt1] > size[rt2] ? sz - size[rt2] : size[rt1];
tmp2 = size[rt2] > size[rt1] ? sz - size[rt1] : size[rt2];
Dfs1(rt1, rt2, 0, edge1[nrt].w), Dfs1(rt2, rt1, 1, 0);
sz = tmp1, mn = m + 1, Getroot(rt1, rt2, -1), Solve(rte);
sz = tmp2, mn = m + 1, Getroot(rt2, rt1, -1), Solve(rte);
} int Merge(int x, int y, ll del) {
if (!x || !y) return x | y;
ans = max(ans, lmx[x] + rmx[y] - del);
ans = max(ans, lmx[y] + rmx[x] - del);
lmx[x] = max(lmx[x], lmx[y]), rmx[x] = max(rmx[x], rmx[y]);
ls[x] = Merge(ls[x], ls[y], del), rs[x] = Merge(rs[x], rs[y], del);
return x;
} void Dfs2(int u, int ff, ll d) {
int e, v;
ans = max(ans, dis[u] * 2 - d * 2);
for (e = first2[u]; ~e; e = edge2[e].next)
if ((v = edge2[e].to) != ff) {
Dfs2(v, u, d + edge2[e].w);
rt[u] = Merge(rt[u], rt[v], d + d);
}
} int main() {
int i, u, v, w;
memset(head, -1, sizeof(head));
memset(first1, -1, sizeof(first1));
memset(first2, -1, sizeof(first2));
ans = -inf, In(n), m = n;
for (i = 1; i < n; ++i) In(u), In(v), In(w), Addpre(u, v, w);
cnt = 0;
for (i = 1; i < n; ++i) In(u), In(v), In(w), Add2(u, v, w);
cnt = 0, Rebuild(1, 0);
sz = m, mn = m + 1, Getroot(1, 0, -1);
Solve(rte), Dfs2(1, 0, 0);
printf("%lld\n", ans >> 1);
return 0;
}

UOJ#400. 【CTSC2018】暴力写挂的更多相关文章

  1. 【UOJ#400】暴力写挂

    题目链接 题意 两棵树 , 求出下面式子的最大值. \[dep[u]+dep[v]-dep[LCA(u,v)]-dep'[LCA'(u,v)]\] Sol 边分治. 与第一棵树有关的信息比较多,所以对 ...

  2. [CTSC2018]暴力写挂——边分树合并

    [CTSC2018]暴力写挂 题面不错 给定两棵树,两点“距离”定义为:二者深度相加,减去两棵树上的LCA的深度(深度指到根节点的距离) 求最大的距离. 解决多棵树的问题就是降维了. 经典的做法是边分 ...

  3. [LOJ#2553][CTSC2018]暴力写挂

    [LOJ#2553][CTSC2018]暴力写挂 试题描述 temporaryDO 是一个很菜的 OIer .在 4 月,他在省队选拔赛的考场上见到了<林克卡特树>一题,其中 \(k = ...

  4. BZOJ5341: [Ctsc2018]暴力写挂

    BZOJ5341: [Ctsc2018]暴力写挂 https://lydsy.com/JudgeOnline/problem.php?id=5341 分析: 学习边分治. 感觉边分治在多数情况下都能用 ...

  5. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  6. UOJ400/LOJ2553 CTSC2018 暴力写挂 边分治、虚树

    传送门--UOJ 传送门--LOJ 跟隔壁通道是一个类型的 要求的式子中有两个LCA,不是很方便,因为事实上在这种题目中LCA一般都是枚举的对象-- 第二棵树上的LCA显然是动不了的,因为没有其他的量 ...

  7. [CTSC2018]暴力写挂

    题目描述 www.lydsy.com/JudgeOnline/upload/201805/day1(1).pdf 题解 首先来看这个我们要最大化的东西. deep[u]+deep[v]-deep[lc ...

  8. bzoj 5341: [Ctsc2018]暴力写挂

    Description Solution 边分治+边分树合并 这个题很多做法都是启发式合并的复杂度的,都有点卡 以前有个套路叫做线段树合并优化启发式合并,消掉一个 \(log\) 这个题思路类似,建出 ...

  9. 并不对劲的bzoj5341:loj2553:uoj400:p4565:[Ctsc2018]暴力写挂

    题目大意 有两棵\(n\)(\(n\leq366666\))个节点的树,\(T\)和\(T'\),有边权 \(dep(i)\)表示在\(T\)中\(i\)号点到\(1\)号点的距离,\(dep'(i) ...

  10. 题解 「CTSC2018暴力写挂」

    题目传送门 题目大意 给出两个大小为 \(n\) 的树,求出: \[\max\{\text{depth}(x)+\text{depth}(y)-\text{depth}(\text{LCA}(x,y) ...

随机推荐

  1. 阿里云服务器之hexo环境搭建

    上一步主要主要讲解云服务器购买和连接云服务器,以及文件的操作.本文主要讲解利用hexo搭建自己的静态博客,在服务器中建立自己的hexo博客环境,最后达到可以远程访问,以及远程git推送到github. ...

  2. PHP错误——Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes)

    解释是可用内存已耗尽,这关系到PHP的memory_limit的设置问题. 这里有两种方法解决 1.修改php.ini memory_limit = 128 打开终端输入下列bash命令 cd /pr ...

  3. vscode调试typescript

    1.记录一个插件:https://www.npmjs.com/package/ts-node # Locally in your project  npm install -D ts-node npm ...

  4. pytest+jenkins安装+allure导出报告

    环境安装: windows7+64位 pytest:4.0.2 allure的安装:allure的python库pytest-allure-adaptor jenkins的安装:2.138.2 JDK ...

  5. Pycharm 报错 AttributeError: module 'pip' has no attribute 'main'

    1.打开文件packaging_tool.py: D:\Program files\pycharm\PyCharm 2016.3.2\helpers\packaging_tool.py 2.添加导入: ...

  6. 通过System获取java环境变量的路径

    通过System获取java环境变量的路径代码为: import java.io.FileNotFoundException; import java.io.FileOutputStream; imp ...

  7. ES6 rest参数和扩展运算符

    rest参数 ES6引入了rest参数(形式为“…变量名”).其中rest参数搭配的变量是一个数组可以使用数组的一切操作. 例: function rest(...values){ let sum=0 ...

  8. 对于maven创建spark项目的pom.xml配置文件(图文详解)

    不多说,直接上干货! http://mvnrepository.com/ 这里,怎么创建,见 Spark编程环境搭建(基于Intellij IDEA的Ultimate版本)(包含Java和Scala版 ...

  9. HUE配置文件hue.ini 的pig模块详解(图文详解)(分HA集群和非HA集群)

    不多说,直接上干货! 一.默认的pig配置文件 ########################################################################### ...

  10. Cassandra概念学习系列之Cassandra是什么?

    不多说,直接上干货! http://cassandra.apache.org/ Apache Cassandra是一套开源分布式NoSQL数据库系统.它最初由Facebook开发,用于储存收件箱等简单 ...