[CTSC2018]暴力寫掛 邊分治+虛樹
分析
題目大意:給倆樹,求的最大值。
比較不好處理的是
由可以得到
帶進去化簡一下可以得到:
事實上這個東西可以直接用邊分治來處理,假設當前邊是,那麼設,在一次分治中變成要最大化,直接上虛樹即可。
不過複雜度是的,據說出題人卡掉了。
有一種更具技巧性的做法,充分利用了邊分治的性質。
我們發現,邊分治有一個特點,就是每一層分治中,一個點要麼再一條邊的“左邊”,要麼在一條邊的“右邊”,這也是邊分治優於點分治的地方——每一層只有兩顆子樹。
這個時候有一種操作是,根據分治樹的結構每個節點動態地開一顆二叉樹(暫且稱其爲xxcc樹)。如果在分治邊的左邊就把它放左邊,否則把它放右邊。
不難發現這棵樹的結構和功能與動態開點的線段樹是類似的,當然,也支持啓發式合併操作。
那麼我們仍舊處理出,只不過這回將其維護在xxcc樹的每個節點上,然後遍歷另一棵樹,枚舉,那麼只需要處理出子樹內部的最大值即可,這個東西可以在啓發式合併的時候統計出來。
注意處理只有一個點的情況。
複雜度同線段樹合併的複雜度是一樣的,均攤的
代碼
#include<bits/stdc++.h>
const int N = 8e5 + 10, M = N << 1, B = N * 20, inf = 1e9;
const long long oo = 1e18;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
struct Edge {
int to[M], nx[M], w[M], pr[N], tp;
Edge() {tp = 1;}
void add(int u, int v, int _w) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; w[tp] = _w;}
void adds(int u, int v, int w = 0) {add(u, v, w); add(v, u, w);}
}R, T, S;
int lst[N], rt[N], siz[N], *pos[N], ch[B][2], n, sums, mn, tot, sz, G;
long long dep[N], mx[B][2], ans, dec; bool del[N];
void Up(long long &a, long long b) {a = std::max(a, b);}
void ins(int u, int v, int w) {
++tot; T.adds(tot, v, w);
T.adds(lst[u], tot, 0); lst[u] = tot;
}
void Build(int u, int fa, long long de) {
dep[u] = de;
for(int i = R.pr[u], v; i; i = R.nx[i])
if((v = R.to[i]) != fa)
ins(u, v, R.w[i]), Build(v, u, de + R.w[i]);
}
void Dfs(int u, int fa, long long dis, bool p) {
if(u <= n) {
*pos[u] = ++sz;
mx[sz][p] = dis + dep[u];
mx[sz][p ^ 1] = -oo;
pos[u] = &ch[sz][p];
}
for(int i = T.pr[u], v; i;i = T.nx[i])
if(!del[i >> 1] && (v = T.to[i]) != fa)
Dfs(v, u, dis + T.w[i], p);
}
void Rt(int u, int fa) {
siz[u] = 1;
for(int i = T.pr[u], v; i; i = T.nx[i])
if(!del[i >> 1] && (v = T.to[i]) != fa) {
Rt(v, u), siz[u] += siz[v];
int tmp = std::max(siz[v], sums - siz[v]);
if(mn > tmp) mn = tmp, G = i;
}
}
void Div(int u, int pres) {
if(pres == 1) return ;
sums = pres; mn = inf; Rt(u, 0);
del[G >> 1] = true;
int x = T.to[G], y = T.to[G ^ 1], sy = pres - siz[x];
Dfs(x, 0, 0, 0); Dfs(y, 0, T.w[G], 1);
Div(x, siz[x]); Div(y, sy);
}
int Mg(int u, int v) {
if(!u || !v) return u | v;
Up(ans, mx[u][0] + mx[v][1] - dec);
Up(ans, mx[u][1] + mx[v][0] - dec);
mx[u][0] = std::max(mx[u][0], mx[v][0]);
mx[u][1] = std::max(mx[u][1], mx[v][1]);
ch[u][0] = Mg(ch[u][0], ch[v][0]);
ch[u][1] = Mg(ch[u][1], ch[v][1]);
return u;
}
void Work(int u, int fa, long long dis) {
Up(ans, dep[u] - dis << 1);
for(int i = S.pr[u], v; i; i = S.nx[i])
if((v = S.to[i]) != fa) {
Work(v, u, dis + S.w[i]);
dec = dis << 1; rt[u] = Mg(rt[u], rt[v]);
}
}
int main() {
n = ri();
for(int i = 1;i < n; ++i) {
int u = ri(), v = ri(), w = ri();
R.adds(u, v, w);
}
for(int i = 1;i <= n; ++i)
lst[i] = i, pos[i] = &rt[i];
tot = n; Build(1, 0, 0);
Div(1, tot);
for(int i = 1;i < n; ++i) {
int u = ri(), v = ri(), w = ri();
S.adds(u, v, w);
}
Work(1, 0, 0);
printf("%lld\n", ans >> 1);
return 0;
}