传送门

就是求两个点 \(a,b\) 使得 \(dis_1(a,b)+dis_2(a,b)+dis_3(a,b)\) 最大

step1

对第一棵树边分治

那么变成 \(d_1(a)+d_1(b)+dis_2(a,b)+dis_3(a,b)\) 最大

并且 \(a,b\) 属于边分开的不同的集合 \(S,T\)

边分治

对于一条边,算经过这条边的路径的答案

点分治不方便的就是同一棵子树的容斥,而边分治不用考虑

直接边分治显然菊花就卡掉了

所以我们要转二叉树

具体来说就是把一个点的儿子建一棵线段树,添加虚拟节点,点权为其父亲的点权,只有线段树的叶子节点有边权

step2

对第二棵树建虚树

建出 \(S|T\) 的虚树

变成 \(d_1(a)+d_2(a)+d_1(b)+d_2(b)+dis_3(a,b)-2d_2(lca_2(a,b))\)

\(a\in S,b\in T\)

枚举 \(lca\) 那么变成求一个类似于 \(w_1(a)+w_2(b)+dis_3(a,b)\) 的最大值

step3

第三棵树不需要什么,上式类似于一个求最长链的东西

而两棵子树合并之后的最长链的端点一定是原来两棵树的最长链的端点

所以只需要在第三棵树上求距离就好了,然后在第二棵树上 \(dp\) 维护

奉上 \(7.6kb\) 大常数代码

# include <bits/stdc++.h>
using namespace std;
typedef long long ll; const int maxn(1e5 + 5); int cnt1, cnt2, lef[maxn], rig[maxn];
ll ans, val[maxn]; namespace Tree1 {
int n, first[maxn], cnt, deep[maxn], st[20][maxn << 1], lg[maxn << 1], dfn[maxn], idx;
ll dis[maxn]; struct Edge {
int to, next;
ll w;
} edge[maxn << 1]; inline void Add(int u, int v, ll w) {
edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
} inline int Min(int x, int y) {
return deep[x] < deep[y] ? x : y;
} void Dfs(int u, int ff) {
int e, v;
dfn[u] = ++idx, st[0][idx] = u;
for (e = first[u]; ~e; e = edge[e].next)
if ((v = edge[e].to) != ff) {
dis[v] = dis[u] + edge[e].w;
deep[v] = deep[u] + 1;
Dfs(v, u), st[0][++idx] = u;
}
} inline int LCA(int u, int v) {
if (u == v) return u;
u = dfn[u], v = dfn[v];
if (u > v) swap(u, v);
int len;
len = lg[v - u + 1];
return Min(st[len][u], st[len][v - (1 << len) + 1]);
} inline ll Dis(int u, int v) {
if (!u || !v) return -1e18;
int lca;
lca = LCA(u, v);
return val[u] + val[v] + dis[u] + dis[v] - (dis[lca] << 1);
} void Init() {
int u, v, i, j;
ll w;
memset(first, -1, sizeof(first));
for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add(u, v, w);
Dfs(1, 0);
for (i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
for (j = 1; j <= lg[idx]; ++j)
for (i = 1; i + (1 << j) - 1 <= idx; ++i)
st[j][i] = Min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
}
} namespace Tree2 {
int n, first[maxn], cnt, deep[maxn], st[20][maxn << 1], lg[maxn << 1], dfn[maxn], idx, dfx[maxn], pid;
int head[maxn], que[maxn], len, size[maxn], sta[maxn], vis[maxn];
ll dis[maxn]; struct Edge {
int to, next;
ll w;
} edge[maxn << 1], edg[maxn << 1]; inline void Add1(int u, int v, ll w) {
edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
} inline void Add2(int u, int v) {
edg[cnt] = (Edge){v, head[u]}, head[u] = cnt++;
} inline int Min(int x, int y) {
return deep[x] < deep[y] ? x : y;
} void Dfs(int u, int ff) {
int e, v;
dfn[u] = ++idx, st[0][idx] = u, dfx[u] = ++pid, size[u] = 1;
for (e = first[u]; ~e; e = edge[e].next)
if ((v = edge[e].to) != ff) {
dis[v] = dis[u] + edge[e].w;
deep[v] = deep[u] + 1;
Dfs(v, u), st[0][++idx] = u;
size[u] += size[v];
}
} inline int LCA(int u, int v) {
if (u == v) return u;
u = dfn[u], v = dfn[v];
if (u > v) swap(u, v);
int len;
len = lg[v - u + 1];
return Min(st[len][u], st[len][v - (1 << len) + 1]);
} struct Info {
int u, v;
ll dis; inline Info(int _u = 0, int _v = 0) {
u = _u, v = _v, dis = Tree1 :: Dis(u, v);
} inline bool operator <(Info b) const {
return dis < b.dis;
}
} f1[maxn], f2[maxn]; inline Info operator +(Info a, Info b) {
return max(max(max(a, b), max(Info(a.u, b.u), Info(a.u, b.v))), max(Info(a.v, b.u), Info(a.v, b.v)));
} inline Info Merge(Info a, Info b) {
return max(max(Info(a.u, b.u), Info(a.u, b.v)), max(Info(a.v, b.u), Info(a.v, b.v)));
} void Init() {
int u, v, i, j;
ll w;
memset(first, -1, sizeof(first));
for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add1(u, v, w);
Dfs(1, 0);
for (i = 2; i <= idx; ++i) lg[i] = lg[i >> 1] + 1;
for (j = 1; j <= lg[idx]; ++j)
for (i = 1; i + (1 << j) - 1 <= idx; ++i)
st[j][i] = Min(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
} inline int Cmp(int x, int y) {
return dfx[x] < dfx[y];
} void Calc(int u, ll add) {
int e, v;
f1[u] = f2[u] = Info();
if (vis[u]) (vis[u] == 1) ? f1[u] = Info(u, u) : f2[u] = Info(u, u);
for (e = head[u]; ~e; e = edg[e].next) {
v = edg[e].to, Calc(v, add);
ans = max(ans, max(Merge(f1[u], f2[v]), Merge(f1[v], f2[u])).dis + add - (dis[u] << 1));
f1[u] = f1[u] + f1[v], f2[u] = f2[u] + f2[v];
}
} void Solve(ll add) {
int i, t, tp;
len = cnt = tp = 0;
for (i = 1; i <= cnt1; ++i) que[++len] = lef[i], vis[lef[i]] = 1;
for (i = 1; i <= cnt2; ++i) que[++len] = rig[i], vis[rig[i]] = 2;
for (i = 1; i <= len; ++i) val[que[i]] += dis[que[i]];
sort(que + 1, que + len + 1, Cmp), t = len;
for (i = 1; i < t; ++i) que[++len] = LCA(que[i], que[i + 1]);
sort(que + 1, que + len + 1, Cmp);
len = unique(que + 1, que + len + 1) - que - 1;
for (i = 1; i <= len; ++i) head[que[i]] = -1;
for (i = 1; i <= len; ++i) {
while (tp && dfx[sta[tp]] + size[sta[tp]] <= dfx[que[i]]) --tp;
if (tp) Add2(sta[tp], que[i]);
sta[++tp] = que[i];
}
Calc(sta[1], add);
for (i = 1; i <= len; ++i) val[que[i]] = vis[que[i]] = 0;
}
} namespace Tree3 {
int n, first[maxn << 2], cnt, head[maxn], tmp[maxn << 2], len, m;
int size[maxn << 2], vis[maxn << 2], rte, sz, mn, st1[maxn], tp1, st2[maxn], tp2;
ll dis[maxn << 2], fw[maxn << 2]; struct Edge {
int to, next;
ll w;
} edge[maxn * 8], edg[maxn << 1]; inline void Add1(int u, int v, ll w) {
edg[cnt] = (Edge){v, head[u], w}, head[u] = cnt++;
edg[cnt] = (Edge){u, head[v], w}, head[v] = cnt++;
} inline void Add2(int u, int v, ll w) {
edge[cnt] = (Edge){v, first[u], w}, first[u] = cnt++;
edge[cnt] = (Edge){u, first[v], w}, first[v] = cnt++;
} int Build(int l, int r) {
if (l > r) return 0;
if (l == r) return tmp[l];
int mid, ls, rs, cur;
mid = (l + r) >> 1, cur = ++n;
ls = Build(l, mid), rs = Build(mid + 1, r);
if (ls) Add2(cur, ls, ls <= m ? fw[ls] : 0);
if (rs) Add2(cur, rs, rs <= m ? fw[rs] : 0);
return cur;
} void Rebuild(int u, int ff) {
int e, v, mid, ls, rs;
len = 0;
for (e = head[u]; ~e; e = edg[e].next)
if ((v = edg[e].to) != ff) fw[v] = edg[e].w, tmp[++len] = v;
mid = (len + 1) >> 1;
ls = Build(1, mid), rs = Build(mid + 1, len);
if (ls) Add2(u, ls, ls <= m ? fw[ls] : 0);
if (rs) Add2(u, rs, rs <= m ? fw[rs] : 0);
for (e = head[u]; ~e; e = edg[e].next)
if ((v = edg[e].to) != ff) Rebuild(v, u);
} void Getroot(int u, int ff, int fe) {
int e, v;
size[u] = 1;
for (e = first[u]; ~e; e = edge[e].next)
if ((v = edge[e].to) != ff && !vis[e]) Getroot(v, u, e), size[u] += size[v];
if (abs(sz - size[u] - size[u]) < mn) rte = fe, mn = abs(sz - size[u] - size[u]);
} void Dfs(int u, int ff, ll d) {
int e, v;
if (u <= m) st1[++tp1] = u;
dis[u] = d;
for (e = first[u]; ~e; e = edge[e].next)
if ((v = edge[e].to) != ff && !vis[e]) Dfs(v, u, d + edge[e].w);
} void Solve(int e) {
if (e < 0) return;
int tmp1, tmp2, i, u1, u2;
u1 = edge[e].to, u2 = edge[e ^ 1].to;
vis[e] = vis[e ^ 1] = 1, tp1 = tp2 = 0;
Dfs(u1, u2, 0);
while (tp1) st2[++tp2] = st1[tp1], --tp1;
Dfs(u2, u1, 0);
cnt1 = cnt2 = 0;
for (i = 1; i <= tp1; ++i) val[st1[i]] = dis[st1[i]], lef[++cnt1] = st1[i];
for (i = 1; i <= tp2; ++i) val[st2[i]] = dis[st2[i]], rig[++cnt2] = st2[i];
Tree2 :: Solve(edge[e].w);
tmp1 = size[u1] > size[u2] ? sz - size[u2] : size[u1];
tmp2 = size[u2] > size[u1] ? sz - size[u1] : size[u2];
sz = tmp1, mn = n + 1, rte = -1, Getroot(u1, 0, -1), Solve(rte);
sz = tmp2, mn = n + 1, rte = -1, Getroot(u2, 0, -1), Solve(rte);
} void Init() {
int u, v, i;
ll w;
memset(first, -1, sizeof(first));
memset(head, -1, sizeof(head));
m = n;
for (i = 1; i < n; ++i) scanf("%d%d%lld", &u, &v, &w), Add1(u, v, w);
cnt = 0, Rebuild(1, 0);
} void Calc() {
sz = n, mn = n + 1, Getroot(1, 0, -1), Solve(rte);
}
} int main() {
int n;
scanf("%d", &n);
Tree1 :: n = Tree2 :: n = Tree3 :: n = n;
Tree1 :: Init(), Tree2 :: Init(), Tree3 :: Init();
Tree3 :: Calc(), printf("%lld\n", ans);
return 0;
}

UOJ#347. 【WC2018】通道(边分治)的更多相关文章

  1. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

  2. UOJ347 WC2018 通道 边分治、虚树

    传送门 毒瘤数据结构题qwq 设三棵树分别为$T1,T2,T3$ 先将$T1$边分治,具体步骤如下: ①多叉树->二叉树,具体操作是对于每一个父亲,建立与儿子个数相同的虚点,将父亲与这些虚点穿成 ...

  3. $[WC2018]$通道(虚树,边分练习)

    \([WC2018]\)通道(虚树,边分练习) 感受码题的快感 这段时间真的是忙忙忙忙忙,省选之前还是露个脸,免得以后没机会了. 但是我感觉我的博客真的没啥人看,虽然我挺想要有人看的,但是自己真的没啥 ...

  4. [UOJ#348][WC2018]州区划分

    [UOJ#348][WC2018]州区划分 试题描述 小 \(S\) 现在拥有 \(n\) 座城市,第ii座城市的人口为 \(w_i\),城市与城市之间可能有双向道路相连. 现在小 \(S\) 要将这 ...

  5. UOJ#347. 【WC2018】通道 边分治 虚树

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ347.html 题意 有三棵树,边有边权. 对于所有点对 (x,y) 求在三棵树上 x 到 y 的距离之和 ...

  6. UOJ 347(洛谷4220) 【WC2018】通道——随机化

    题目:http://uoj.ac/problem/347 https://www.luogu.org/problemnew/show/P4220 先写了暴力分的44分.那个两棵树.其中一棵是编号连续的 ...

  7. 洛谷P4220 [WC2018]通道(边分治+虚树)

    题面 传送门 题解 代码不就百来行么也不算很长丫 虽然这题随机化贪心就可以过而且速度和正解差不多不过我们还是要好好学正解 前置芝士 边分治 米娜应该都知道点分治是个什么东西,而边分治,顾名思义就是对边 ...

  8. 洛谷 P4220 & UOJ #347 通道 —— 随机化

    题目:https://www.luogu.org/problemnew/show/P4220 http://uoj.ac/problem/347 先写了一下 n^2 和三棵树一样的情况,n^2 还写了 ...

  9. 【UOJ347】【WC2018】通道 边分治 虚树 DP

    题目大意 给你三棵树,点数都是\(n\).求 \[ \max_{i,j}d_1(i,j)+d_2(i,j)+d_3(i,j) \] 其中\(d_k(i,j)\)是在第\(k\)棵数中\(i,j\)两点 ...

随机推荐

  1. Keil RTX使用 os_mut_init 报Hard Fault 错误解决记录

    首先确定你的软件是在互斥信号初始化的位置,在以下几个位置,将会报Hard Fault 错误: (1).os_sys_init_user 用户线程创建之前 (2).os_tsk_create_user之 ...

  2. Python之scrapy框架之post传输数据错误:TypeError: to_bytes must receive a unicode, str or bytes object, got int

    错误名:TypeError: to_bytes must receive a unicode, str or bytes object, got int 错误翻译:类型错误:to_bytes必须接收u ...

  3. Visual Studio 跨平台開發實戰(2) - Xamarin.iOS 基本控制項介紹 (转帖)

    前言 在上一篇文章中, 我們介紹了Xamarin 以及簡單的HelloWorld範例, 這次我們針對iOS的專案目錄架構以及基本控制項進行說明. 包含UIButton,, UISlider, UISw ...

  4. JVM-垃圾收集过程的内存管理

    JDK1.7 JVM的垃圾收集算法有 1. 标记-清除算法: 2. 复制算法:在商业虚拟机都是使用这种算法来回收新生代的 3. 标记-整理算法: 4.分代收集算法: JDK1.7 JVM的垃圾收集器有 ...

  5. sql在insert时判断有无唯一性冲突

    sql在insert时判断有无唯一性冲突,存在相同主键或唯一索引,则不创建 INSERT INTO table(field1, field2, fieldn) SELECT 'field1', 'fi ...

  6. vuex requires a Promise polyfill in this browser

    ie 浏览器访问 vue 项目(使用的vuex 状态管理组件)报错:vuex requires a Promise polyfill in this browser 处理办法: 1.npm insta ...

  7. python-select异步IO

    #实现多任务在同一个线程切换 #!/usr/bin/python from socket import * from select import * from time import ctime so ...

  8. unity字库精简

    有2种办法,具体看情况使用 1.unity自带功能 选择放入的字体,修改Character项为"Custom set",接着出现Custom Chars中输入你想使用的字符串,字符 ...

  9. C 扩展库 - mysql API CRUD

    CRUD table create table if not exists `student` ( `id` int auto_increment, `name` varchar(16) not nu ...

  10. Go RabbitMQ(三)发布订阅模式

    RabbitMQ 在上一节中我们创建了工作队列,并且假设每一个任务都能够准确的到达对应的worker.在本节中我们将介绍如何将一个消息传递到多个消费者,这也就是所说的发布订阅模式 为了验证该模式我们使 ...