题意

给你两个有 \(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. [ArcGIS API for JavaScript 4.8] Sample Code-Get Started-widgets简介

    [官方文档:https://developers.arcgis.com/javascript/latest/sample-code/intro-widgets/index.html] 一.Intro ...

  2. Android使用Mob ShareSDK 分享不同平台

    Share分享,要注意的是,如果不能调用起微信相关,看看微信开发者平台是否添加正式,微信开发者正式之后,打正式包进行分享调用 /*分享弹框*/ private void showShare2() { ...

  3. geth搭建私有网络

    geth --rpc --unlock "3ae88fe370c39384fc16da2c9e768cf5d2495b48,81063419f13cab5ac090cd8329d8fff9f ...

  4. SQL查询获得指定格式内容

    Oracle中通过修改SQL语句,达到将查询的内容拼接为指定的字符串格式 eg: select '<ta:datagridItem id="' || lower(column_name ...

  5. UPUPW配置

    UPUPW下载地址 https://sourceforge.net/projects/upupw/files/ANK/?tdsourcetag=s_pctim_aiomsg 配置 在这里插入图片描述

  6. MongoDB基础学习

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  7. MFC界面相关源码

    这是这4篇MFC界面的相关源码.建议学习Visual C++的看看这2本微软官方出的教材. [MFC Windows程序设计(第2版,修订版)](美)Jeff Prosise著 [Windows程序设 ...

  8. python文章装饰器理解12步

    1. 函数 在python中,函数通过def关键字.函数名和可选的参数列表定义.通过return关键字返回值.我们举例来说明如何定义和调用一个简单的函数: def foo(): return 1 fo ...

  9. 超哥笔记 -- 用户管理、权限设置、进程管理、中文配置、计划任务和yum源配置(5)

    一 网卡配置 ifconfig 查询.设置网卡和ip等参数 ifup,ifdown    脚本命令,更简单的方式启动关闭网络 ip 符合指令,直接修改上述功能 网络配置文件: /etc/sysconf ...

  10. .NET CORE学习笔记系列(2)——依赖注入[6]: .NET Core DI框架[编程体验]

    原文https://www.cnblogs.com/artech/p/net-core-di-06.html 毫不夸张地说,整个ASP.NET Core框架是建立在一个依赖注入框架之上的,它在应用启动 ...