P4565 [CTSC2018]暴力写挂(边分树)
P4565 [CTSC2018]暴力写挂(边分树)
题目大意
给定两棵树,边有权,求下式的最大值
\[\mathrm{depth}(x) + \mathrm{depth}(y) - ({\mathrm{depth}(\mathrm{LCA}(x,y))}+{\mathrm{depth'}(\mathrm{LCA'}(x,y))})
\]
数据范围
对于所有数据, \(n \le 366666 , |v| \le 2017011328\) 。 详细数据范围见下表,表格中的“无” 表示无特殊限制。
测试点编号 | \(n \le\) | v | T 是一条链 | T' 是一条链 |
---|---|---|---|---|
1 | 36 | =1 | 否 | 否 |
2 | 366 | =1 | 否 | 否 |
3 | 1388 | >0 | 否 | 否 |
4 | 1999 | >0 | 否 | 否 |
5 | 2666 | >0 | 否 | 否 |
6 | 5666 | 无 | 否 | 否 |
7 | 8666 | 无 | 否 | 否 |
8 | 11111 | 无 | 否 | 否 |
9 | 12345 | 无 | 否 | 否 |
10 | 366666 | >0 | 是 | 是 |
11 | 366666 | 无 | 是 | 是 |
12~13 | 366666 | >0 | 是 | 否 |
14 | 366666 | 无 | 是 | 否 |
15~16 | 366666 | >0 | 否 | 是 |
17 | 366666 | 无 | 否 | 是 |
18~20 | 366666 | 无 | 否 | 否 |
\(depth(p)\) 和 $depth'(p) $分别表示树 \(T\) 、 \(T'\) 中点 \(1\) 到点 \(p\) 的距离,这里规定,距离指的是经过的边的边权总和,其中 \(\mathrm{depth}(1) = 0\) 。
\(LCA(x, y)\) 和 \(LCA'(x, y)\) 分别表示树 \(T\) 、 \(T'\) 中点 \(x\) 与点 \(y\) 的最近公共祖先,即在从 \(x\) 到 \(y\) 的最短路径上的距离根经过边数最少的点
解题思路
边分树合并模板题,主要是一些细节
这个式子我们可以化为(其实不化也可做)
\[Ans = \frac 12 \max(dis(x, y)+dep(x)+dep(y)-2*dep'(lca'(x,y)))
\]
考虑在第二棵树上枚举 lca,同时边分治合并统计前半部分的最大值,主要还是一些细节和实现方式
可以考虑使用 namespace 肆意使用重复的变量名
代码
const int N = 400250;
struct Tree {
int ls, rs;
ll vl, vr;
Tree() { vl = vr = -1e15; }
#define ls(p) tree[p].ls
#define rs(p) tree[p].rs
#define vl(p) tree[p].vl
#define vr(p) tree[p].vr
}tree[N<<5];
int rt[N], las[N], nodecnt;
namespace Conquer {
const int N = 1500005;
int h[N], ne[N<<1], to[N<<1]; ll w[N<<1], dis[N], tot = 1, cnt;
inline void add(int x, int y, ll z) {
ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
}
inline void adde(int x, int y, ll z) { add(x, y, z), add(y, x, z); }
void get_dis(int x, int fa) {
for (int i = h[x], y; i; i = ne[i])
if ((y = to[i]) != fa)
dis[y] = dis[x] + w[i], get_dis(y, x);
}
int vis[N], siz[N], Siz, lim, ed, n;
void get(int x, int fa) {
siz[x] = 1;
for (int i = h[x], y; i; i = ne[i]) {
if ((y = to[i]) == fa || vis[i]) continue;
get(y, x), siz[x] += siz[y];
int tp = max(siz[y], Siz - siz[y]);
if (tp < lim) lim = tp, ed = i;
}
}
void dfs(int x, int fa, ll Dis, bool tp) {
if (x <= n) {
++nodecnt;
if (ls(las[x]) == -1) ls(las[x]) = nodecnt;
else rs(las[x]) = nodecnt;
if (!tp) ls(nodecnt) = -1, vl(nodecnt) = Dis + dis[x];
else rs(nodecnt) = -1, vr(nodecnt) = Dis + dis[x];
las[x] = nodecnt;
}
for (int i = h[x], y; i; i = ne[i])
if ((y = to[i]) != fa && !vis[i])
dfs(y, x, Dis + w[i], tp);
}
void conquer(int x, int S) {
if (S <= 1) return; Siz = lim = S, get(x, 0);
vis[ed] = vis[ed^1] = 1; int tx = to[ed], ty = to[ed^1];
dfs(tx, 0, 0, 0), dfs(ty, 0, w[ed], 1);
conquer(ty, S - siz[tx]), conquer(tx, siz[tx]);
}
void main() {
for (int i = 1;i <= n; i++)
rt[i] = las[i] = ++nodecnt, ls(rt[i]) = -1;
get_dis(1, 0), conquer(1, cnt);
}
}
namespace T2 {
int h[N], ne[N<<1], to[N<<1]; ll w[N<<1], tot;
inline void add(int x, int y, ll z) {
ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
}
ll ans, now;
int merge(int x, int y) {
if (!x || !y) return x | y;
Mx(ans, max(vl(x) + vr(y), vr(x) + vl(y)) - now);
ls(x) = merge(ls(x), ls(y));
rs(x) = merge(rs(x), rs(y));
Mx(vl(x), vl(y)), Mx(vr(x), vr(y));
return x;
}
void dfs(int x, int fa, ll dis) {
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (y == fa) continue;
dfs(y, x, dis + w[i]), now = 2 * dis;
rt[x] = merge(rt[x], rt[y]);
}
Mx(ans, (Conquer::dis[x] - dis) * 2);
}
void main(void) {
dfs(1, 0, 0), write(ans / 2);
}
}
ll h[N], ne[N<<1], to[N<<1]; ll w[N<<1], tot;
inline void add(int x, int y, ll z) {
ne[++tot] = h[x], to[h[x] = tot] = y, w[tot] = z;
}
int Las[N], cnt;
void dfs(int x, int fa) {
for (int i = h[x], y; i; i = ne[i]) {
if ((y = to[i]) == fa) continue; dfs(y, x);
if (!Las[x]) Conquer::adde(x, y, w[i]), Las[x] = x;
else {
Conquer::adde(Las[x], ++cnt, 0);
Conquer::adde(Las[x] = cnt, y, w[i]);
}
}
}
signed main() {
int n; read(n), cnt = n;
for (int i = 1, x, y, z;i < n; i++)
read(x), read(y), read(z), add(x, y, z), add(y, x, z);
for (int i = 1, x, y, z;i < n; i++)
read(x), read(y), read(z), T2::add(x, y, z), T2::add(y, x, z);
dfs(1, 0), Conquer::cnt = cnt, Conquer::n = n, Conquer::main(), T2::main();
return 0;
}