题意

给你两个有 \(n\) 个点的树 \(T, T'\) ,求一对点对 \((x, y)\) 使得

\[depth(x) + depth(y) - (depth(LCA(x , y)) + depth′ (LCA′ (x, y)))
\]

最大。

数据范围

对于所有数据, \(n \le 366666 , |v| \le 2017011328\) 。

题解

第一次写边分治(原来碰到过都弃疗啦) 。

我们看这个式子不太舒服,化简一下:

\[\frac 1 2 (dist(x, y) - depth(x) - depth(y) + 2depth′ (LCA′ (x, y)) )
\]

这样的话,我们可以考虑枚举第二棵树的 \(LCA'\) ,那么我们意味着需要在这棵树的两个子树内找到点对 \((x, y)\) 使得它们的 \(dist(x, y) - depth(x) - depth(y)\) 最大。

怎么做呢,我们可以考虑边分治。边分治有什么好处呢?每次分治的话只有两边。

但是为了让复杂度正确,我们考虑使用二叉化,把度数降下来。不然可能在菊花处退化到 \(\mathcal O(n^2)\) 。

具体来说可以依次考虑每个儿子,然后每次拆掉原来的边,搞个新点和新边保证相对关系不变就行了,这样的点数好像是浪费最少的,时间和空间都比较优秀。

假设我们当前分治的两边是 \(U, V\) ,分治重心边的节点是 \(u, v\) 那么我们只需要找到一对点 \(p_1 \in U, p_2 \in V\) 使得 \(dist(p_1, u) + dist(p_2, v) + depth(p_1) + depth(p_2)\) 最大。

我们可以先每次分治预处理出每个分治块内的点到分治中心边的距离,那么我们对于每个点有个 \(dist(x, p) + dep_x\) 的信息。

那么对于每个点 \(x\) 会有 \(\log\) 个信息,我们记下每次相对于分治边的方向,那么可以唯一确定这个信息所在的分治块的位置!

这样有什么好处呢?我们在第二棵树上直接对于每个点的 \(\log\) 信息合并,那么这次合并的节点刚好对应上第一棵树上同一个分治块,这个类似于线段树合并的操作,用左边和右边的信息合并就行了。

这样复杂度是 \(\mathcal O(n \log n)\) ,注意空间也是 \(\mathcal O(n \log n)\) 的,要卡下空间。

代码

参考了 zhoushuyu 神仙的代码 ,写的好精简啊QAQ

#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
#define Travel(i, u, G) for (int i = G.Head[u], v = G.to[i], w = G.val[i]; i; i = G.Next[i], v = G.to[i], w = G.val[i])
#define fir first
#define sec second 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 ("2553.in", "r", stdin);
freopen ("2553.out", "w", stdout);
#endif
} const int N = 366666 + 233, NN = N * 3;
const ll inf = 0x3f3f3f3f3f3f3f3f; template<int Maxn, int Maxm>
struct Graph { int Head[Maxn], Next[Maxm], to[Maxm], val[Maxm], e; Graph() { e = 1; } inline void add_edge(int u, int v, int w) {
to[++ e] = v; Next[e] = Head[u]; Head[u] = e; val[e] = w;
} inline void Add(int u, int v, int w) {
add_edge(u, v, w); add_edge(v, u, w);
} }; Graph<N, N << 1> T1, T2;
Graph<NN, N << 3> DT; int node, n; ll dep[N]; void Build(int u, int fa = 0) {
int lst = u;
Travel(i, u, T1) if (v != fa) {
dep[v] = dep[u] + w, Build(v, u);
DT.Add(lst, ++ node, 0), DT.Add(node, v, w), lst = node;
}
} int minsz, id, sz[NN]; bool vis[N << 3]; void Get_Edge(int u, int fa, int tot) {
sz[u] = 1;
Travel(i, u, DT) if (v != fa && !vis[i]) {
Get_Edge(v, u, tot); sz[u] += sz[v];
if (chkmin(minsz, max(sz[v], tot - sz[v]))) id = i;
}
} const int Node = (NN) * 20; int ch[Node][2], stot; ll val[Node][2]; PII info[NN]; int rt[NN]; void Get_Info(int u, int fa, ll dis, int dir) {
if (u <= n) {
++ stot;
if (!info[u].fir) rt[u] = stot;
else ch[info[u].fir][info[u].sec] = stot;
info[u] = make_pair(stot, dir);
val[stot][dir] = dis + dep[u];
val[stot][dir ^ 1] = - inf;
}
sz[u] = 1;
Travel(i, u, DT) if (v != fa && !vis[i])
Get_Info(v, u, dis + w, dir), sz[u] += sz[v];
} int maxdep;
void Solve(int u, int tot) {
if (tot == 1) return;
minsz = node + 1; Get_Edge(u, 0, tot);
int x = DT.to[id], y = DT.to[id ^ 1];
vis[id] = vis[id ^ 1] = true;
Get_Info(x, 0, 0, 0);
Get_Info(y, 0, DT.val[id], 1);
Solve(x, sz[x]); Solve(y, sz[y]);
} ll ans = - inf; int Merge(int x, int y, ll dec) {
if (!x || !y) return x | y;
chkmax(ans, max(val[x][0] + val[y][1], val[x][1] + val[y][0]) - dec);
Rep (i, 2) chkmax(val[x][i], val[y][i]), ch[x][i] = Merge(ch[x][i], ch[y][i], dec);
return x;
} void Dfs(int u, int fa = 0, ll dis = 0) {
chkmax(ans, (dep[u] - dis) * 2);
Travel(i, u, T2) if (v != fa) {
Dfs(v, u, dis + w);
rt[u] = Merge(rt[u], rt[v], 2 * dis);
}
} int main () { File(); node = n = read();
For (i, 1, n - 1) {
int u = read(), v = read(), w = read(); T1.Add(u, v, w);
}
For (i, 1, n - 1) {
int u = read(), v = read(), w = read(); T2.Add(u, v, w);
} Build(1); Solve(1, node); Dfs(1); printf ("%lld\n", ans >> 1); return 0; }

LOJ #2533. 「CTSC2018」暴力写挂(边分治合并)的更多相关文章

  1. LOJ 2553 「CTSC2018」暴力写挂——边分治+虚树

    题目:https://loj.ac/problem/2553 第一棵树上的贡献就是链并,转化成 ( dep[ x ] + dep[ y ] + dis( x, y ) ) / 2 ,就可以在第一棵树上 ...

  2. Loj #2553. 「CTSC2018」暴力写挂

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

  3. 「CTSC2018」暴力写挂

    毫无$ Debug$能力 全世界就我会被卡空间.jpg LOJ #2553 UOJ #400 Luogu P4565 题意 给定两棵树$ T,T'$,求一组点对$ (x,y)$使得$deep(x)+d ...

  4. UOJ#400. 【CTSC2018】暴力写挂 边分治 线段树合并

    原文链接 www.cnblogs.com/zhouzhendong/p/UOJ400.html 前言 老年选手没有码力. 题解 先对第一棵树进行边分治,然后,设点 x 到分治中心的距离为 $D[x]$ ...

  5. loj#2552. 「CTSC2018」假面

    题目链接 loj#2552. 「CTSC2018」假面 题解 本题严谨的证明了我菜的本质 对于砍人的操作好做找龙哥就好了,blood很少,每次暴力维护一下 对于操作1 设\(a_i\)为第i个人存活的 ...

  6. 【CTSC2018】暴力写挂(边分治,虚树)

    [CTSC2018]暴力写挂(边分治,虚树) 题面 UOJ BZOJ 洛谷 题解 发现第二棵树上的\(LCA\)的深度这玩意没法搞,那么枚举在第二棵树上的\(LCA\). 然后剩下的部分就是\(dep ...

  7. Loj #2554. 「CTSC2018」青蕈领主

    Loj #2554. 「CTSC2018」青蕈领主 题目描述 "也许,我的生命也已经如同风中残烛了吧."小绿如是说. 小绿同学因为微积分这门课,对"连续"这一概 ...

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

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

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

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

随机推荐

  1. GDB 显示别的文件;在别文件打断点;执行到函数末尾;跳出当前函数

    显示别的文件:l "文件名.后缀名":行号 在别文件打断点:b "文件名.后缀名":行号 执行到函数末尾:finish 跳出当前函数(当前位置到函数的末尾不被执 ...

  2. Saltstack_使用指南04_数据系统-Grains

    1. 主机规划 Grains文档 https://docs.saltstack.com/en/latest/topics/grains/index.html 注意事项 修改了master或者minio ...

  3. 使用Visual Studio Code进行ABAP开发

    长期以来,我们都使用SAP GUI进行ABAP编码工作,事务代码SE38甚至成了ABAP的代名词. SAP GUI的代码编辑能力和一些专业的IDE比较起来难免相形见绌,为了给开发者们更好的体验,SAP ...

  4. 合并两个有序链表的golang实现

    将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 输入:->->, ->-> 输出:->->->->-> ...

  5. tomcat报异常Invalid character found in method name. HTTP method names must be tokens

    最近监控了一下测试环境的日志,突然出现如下一个异常 由Error parsing HTTP request header可以看出是由于解析请求头出错导致的,但是它属于DEBUG级别的异常,虽然不影响系 ...

  6. python之sqlalchemy的使用

    准备数据 from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column from sqla ...

  7. kafka-rest:A Comprehensive, Open Source REST Proxy for Kafka

    Ewen Cheslack-Postava  March 25, 2015  时间有点久,但讲的还是很清楚的 As part of Confluent Platform 1.0 released ab ...

  8. 关于出现Specified VM install not found: type Standard VM, name jdk1.5.0_04问题的解决办法

    问题出现背景: 今天把原来电脑中的jdk版本由1.7换到了1.8,,重新在Eclipse中配置环境后出现了此问题 问题解决办法: 在你的项目中,选中ant文件,右键>>Run As > ...

  9. 2018-2019-2 20175332-实验一《Java开发环境的熟悉》实验报告

    一.安装IDEA 1.在官方网站下载IDEA安装包https://www.jetbrains.com/idea/download/#section=windows 2.破解软件,第一次参考博客是:ht ...

  10. 如何将Bitcoin比特币区块链数据导入关系数据库

    在接触了比特币和区块链后,我一直有一个想法,就是把所有比特币的区块链数据放入到关系数据库(比如SQL Server)中,然后当成一个数据仓库,做做比特币交易数据的各种分析.想法已经很久了,但是一直没有 ...