P4220 通道笔记
边分治神题。
前置知识:边分治,虚树。
题意
给定
题解
考虑边分治。下面用
对
所以现在枚举
我们发现,最长路竟然是可以合并的!也就是说:设
于是这题就做完了,时间复杂度
代码
有点难写,调了 1.5h。。。感叹代码能力的弱小。代码最下面的“类人群星闪耀时”是我写错了哪些。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 2E5 + 5;
int n; i64 d[3][N], sd[N], ans; bool L[N], R[N];
vector <pair <int, i64>> G[4][N];
struct t3 {
int yf[N][20], S = 19, dep[N]; i64 dd[N], bq[N];
void dfs(int x, int fa) {
for (auto [v, w] : G[3][x]) {
if (v == fa) continue;
dd[v] = dd[x] + w; yf[v][0] = x;
dep[v] = dep[x] + 1;
for (int i = 1; i <= S; ++i) yf[v][i] = yf[yf[v][i - 1]][i - 1];
dfs(v, x);
}
}
int glca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = S; i >= 0; --i) if (dep[u] - (1 << i) >= dep[v])
u = yf[u][i];
if (u == v) return u;
for (int i = S; i >= 0; --i) if (yf[u][i] != yf[v][i])
u = yf[u][i], v = yf[v][i];
return yf[u][0];
}
void nd(int x, i64 val) {bq[x] = val;}
i64 dis(int u, int v) {
if (!u || !v) return -1E9;
if (u == v) return 0;
return dd[u] + dd[v] - 2 * dd[glca(u, v)] + bq[u] + bq[v];
}
} pd;
struct virt {
int h[N], m, len, a[N << 1], ll, dfn[N], S = 19, yf[N][20], dep[N], ccnt;
void push(int x) {h[++len] = x;} vector <int> E[N];
void conn(int x, int y) {E[x].emplace_back(y); E[y].emplace_back(x);}
void dfs(int x, int fa) {
dfn[x] = ++ccnt;
for (auto [v, w] : G[2][x]) {
if (v == fa) continue;
d[2][v] = d[2][x] + w;
dep[v] = dep[x] + 1; yf[v][0] = x;
for (int i = 1; i <= S; ++i) yf[v][i] = yf[yf[v][i - 1]][i - 1];
dfs(v, x);
}
}
int glca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = S; ~i; --i) if (dep[u] - (1 << i) >= dep[v])
u = yf[u][i];
if (u == v) return u;
for (int i = S; ~i; --i)
if (yf[u][i] != yf[v][i]) {
u = yf[u][i], v = yf[v][i];
}
return yf[u][0];
}
void build() {
sort(h + 1, h + len + 1, [&](int x, int y) {return dfn[x] < dfn[y];});
for (int i = 1; i <= len; ++i) a[++ll] = h[i];
for (int i = 1; i < len; ++i) {
a[++ll] = glca(h[i], h[i + 1]);
} a[++ll] = 1;
sort(a + 1, a + 1 + ll, [&](int x, int y) {return dfn[x] < dfn[y];});
ll = unique(a + 1, a + 1 + ll) - a - 1;
for (int i = 1; i < ll; ++i) {
int lc = glca(a[i], a[i + 1]);
conn(lc, a[i + 1]);
}
}
void init() {
dep[1] = 1; for (int i = 0; i <= S; ++i) yf[1][i] = 1;
dfs(1, 0);
pd.dep[1] = 1; for (int i = 0; i <= S; ++i)
pd.yf[1][i] = 1;
pd.dfs(1, 0);
}
struct cl {
int p, q; i64 Di;
i64 val() {return Di;}
bool operator < (const cl &w) const {return Di < w.Di;}
cl (int x, int y) {p = x; q = y; Di = pd.dis(p, q);}
cl () {p = q = 0; Di = -1E9; }
} ;
cl merge(cl a, cl b) {
return max({cl(a.p, a.q), cl(a.p, b.q), cl(a.p, b.p), cl(a.q, b.p), cl(a.q, b.q), cl(b.p, b.q)});
}
cl lmg(cl a, cl b) {
return max({cl(a.p, b.p), cl(a.p, b.q), cl(a.q, b.p), cl(a.q, b.q)});
}
pair <cl, cl> dp(int x, int fa) {
cl l, r;
if (L[x]) l = cl(x, x);
else if (R[x]) r = cl(x, x);
for (auto v : E[x]) {
if (v == fa) continue;
auto [tl, tr] = dp(v, x);
ans = max(ans, lmg(l, tr).val() - 2 * d[2][x]);
ans = max(ans, lmg(r, tl).val() - 2 * d[2][x]);
l = merge(l, tl);
r = merge(r, tr);
}
return make_pair(l, r);
}
void solve() {
build();
for (int i = 1; i <= len; ++i) if (L[h[i]] || R[h[i]])
pd.nd(h[i], d[1][h[i]] + d[2][h[i]]);
dp(1, 0);
}
void clear() {
for (int i = 1; i <= ll; ++i)
E[a[i]].clear();
len = ll = 0;
}
} vi;
struct bfz {
int sz[N], root, dmx, dsum, head[N], nex[N << 1], tot = 1, m; bool vis[N];
struct E {int to, nxt; i64 dis;} edge[N << 1];
void add(int x, int y, i64 z) {
edge[++tot] = (E) {x, y, z};
nex[tot] = head[x]; head[x] = tot;
}
void getroot(int x, int fa) {
sz[x] = 1;
for (int i = head[x]; i; i = nex[i]) {
int v = edge[i].nxt;
if (v == fa || vis[i >> 1]) continue;
getroot(v, x); sz[x] += sz[v];
int tmp = max(sz[v], dsum - sz[v]);
if (dmx > tmp) {
dmx = tmp;
root = i;
}
}
}
void rebuild(int x, int fa) {
int tmp = 0, len = G[1][x].size(), last;
for (auto [v, w] : G[1][x]) {
if (v == fa) continue;
++tmp; if (tmp == 1) {
add(v, x, w); add(x, v, w);
last = x;
} else if (tmp == len - (x != 1)) {
add(last, v, w); add(v, last, w);
} else {
++m;
add(last, m, 0); add(m, last, 0);
last = m;
add(m, v, w); add(v, m, w);
}
}
for (auto [v, w] : G[1][x]) if (v != fa)
rebuild(v, x);
}
void dfs(int x, int fa, bool typ) {
if (x <= n) vi.push(x);
if (!typ) L[x] = 1;
else R[x] = 1;
for (int i = head[x]; i; i = nex[i]) {
int v = edge[i].nxt; i64 w = edge[i].dis;
if (v == fa || vis[i >> 1]) continue;
d[1][v] = d[1][x] + w;
dfs(v, x, typ);
}
}
void clear(int x, int fa) {
L[x] = R[x] = 0;
for (int i = head[x]; i; i = nex[i]) {
int v = edge[i].nxt;
if (v == fa || vis[i >> 1]) continue;
clear(v, x);
}
}
int cnt = 0;
void solve(int x, int nsum) {
root = 0; dmx = 1E9; dsum = nsum;
getroot(x, 0); if (!root) return ;
vis[root >> 1] = 1;
int u = edge[root].to, v = edge[root].nxt;
i64 w = edge[root].dis;
d[1][u] = 0; dfs(u, v, 0); d[1][v] = w; dfs(v, u, 1);
vi.solve(); vi.clear();
clear(u, v); clear(v, u);
solve(u, dsum - sz[v]);
solve(v, sz[v]);
}
void solve() {
m = n;
rebuild(1, 0);
solve(1, m);
}
} b;
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= 3; ++i) {
for (int j = 1; j < n; ++j) {
int u, v; i64 w; cin >> u >> v >> w;
G[i][u].emplace_back(v, w);
G[i][v].emplace_back(u, w);
}
} vi.init();
b.solve();
cout << ans << '\n';
return 0;
}
/*
类人群星闪耀时:
1. E 未清空。
2. 虚树中加入了三度化的虚点。
3. 更新答案时,没注意 a∈L,b∈R 的条件。
4. d[1][x] 忘记清空。
5. 十年OI一场空,______________。
*/
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析