UOJ#347. 【WC2018】通道(邊分治)
阿新 • • 發佈:2019-01-05
傳送門
就是求兩個點 \(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; }