题意

给你两个有 \(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. 在IIS上部署你的ASP.NET Core 2.1项目

    1.在控制面板→程序→启用或关闭Windows功能→勾选Internet Information Services以及Web管理工具下的IIS管理控制台 2.IIS 安装AspNetCoreModul ...

  2. iOS----------常用三方库

    1.笔者常用三方库 名称 作用 说明 <small>AFNetworking <small>基于HTTP/HTTPS 联网请求 <small> <small& ...

  3. Mysql增量写入Hdfs(一) --将Mysql数据写入Kafka Topic

    一. 概述 在大数据的静态数据处理中,目前普遍采用的是用Spark+Hdfs(Hive/Hbase)的技术架构来对数据进行处理. 但有时候有其他的需求,需要从其他不同数据源不间断得采集数据,然后存储到 ...

  4. centos7中/tmp文件保存天数

    不要在/tmp目录下保存文件,该目录会定期清理文件 /tmp默认保存10天 /var/tmp默认保存30天 配置文件:/usr/lib/tmpfiles.d/tmp.conf 默认配置文件:# Thi ...

  5. TinScrapy-简化的Scrapy原码-查看爬虫的执行流程

    学习了自定义的TinyScrapy框架,整理出以下定注释的代码 from twisted.web.client import getPage,defer from twisted.internet i ...

  6. EF ORM

    //新增 UserInfo userInfo = new UserInfo(); userInfo.UserName = "YANG"; userInfo.UserPass = & ...

  7. SQL 创建分区表

    (以项目中实际使用的GNSS库为例) 背景:数据量巨大,定时创建月表存放数据,月表中数据存放在不同的文件组中来提高查询效率   一.创建数据库,添加文件组 除了逻辑文件和物理文件的分离之外,SQL S ...

  8. JavaScript对象类型之简单介绍

    引言 对象是JavaScript的基本数据类型. 对象是一种复合值:将很多值(原始值或者其他对象)聚合在一起,通过名字访问这些值. 对象可以看做属性的无序集合,每个属性都是一个名/值对.属性名是字符串 ...

  9. 三 Struts2 添加返回数据

    一.struts2如何获取servletAPI1.解耦和的方式获取封装后的Map对象,这种获取的map对象只有存取数据的功能. Map request = (Map) ActionContext.ge ...

  10. spring boot拦截器中获取request post请求中的参数(转)

    文章转自 https://www.jianshu.com/p/69c6fba08c92