题意

给你两个有 \(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. C#从SqlServer数据库读写文件源码

    如下的资料是关于C#从SqlServer数据库读写文件的内容,希望能对码农们有一些用. <%@ Page Language="C#" %> <script run ...

  2. 测者的测试技术手册:揭开java method的一个秘密--巨型函数

    揭开java method的一个秘密:巨型函数 相信,很多人都不知道Java的Method的上限为64K.本文将超过这个上限的函数叫做巨型函数. 巨型函数的问题 1.如果代码超过了这个限制,Java编 ...

  3. UE3中的时间

    为了管理时间,Unreal将游戏运行时间片分隔为"Ticks".一个Tick是关卡中所有Actors更新的最小时间单位.一个tick一般是10ms-100ms(CPU性能越好,游戏 ...

  4. MySQL的自动提交模式

      默认情况下, MySQL启用自动提交模式(变量autocommit为ON).这意味着, 只要你执行DML操作的语句,MySQL会立即隐式提交事务(Implicit Commit).这个跟SQL S ...

  5. Lua中string.format占位符的使用

    虽然lua中字符串拼接"string.format"相对于".."消耗较大,但有时为了代码的可读性,项目中还是经常用到"string.format&q ...

  6. 5.2Python数据处理篇之Sympy系列(二)---Sympy的基本操作

    目录 目录 前言 (一)符号的初始化与输出设置-symbol() symbols() latex() 1.作用: 2.操作: (二)替换符号-subs(old,new) 1.说明: 2.源代码: 3. ...

  7. #020PAT 没整明白的题L1-009 N个数求和 (20 分)

    后面的测试点过不去,两个错误一个超时. 目前未解决   L1-009 N个数求和 (20 分)   本题的要求很简单,就是求N个数字的和.麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和 ...

  8. Python基础——0前言

    python虽然这几年才兴起,但是已经是一门“老”语言了. python的诞生历史也很有趣.Python的创始人为Guido van Rossum(龟叔).1989年圣诞节期间,在阿姆斯特丹,Guid ...

  9. BZOJ 3684 大朋友和多叉树

    BZOJ 3684 大朋友和多叉树 Description 我们的大朋友很喜欢计算机科学,而且尤其喜欢多叉树.对于一棵带有正整数点权的有根多叉树,如果它满足这样的性质,我们的大朋友就会将其称作神犇的: ...

  10. mysql 相关命令

    1.mysql导入导出 导出 进入到mysql bin目录 导出表 ./mysqldump -uroot -p --socket=/wdcloud/app/mysql1/temp/mysql.sock ...