[NOI2018]情報中心
分析
即zjoi線圖之後又一道兩百行代碼的題。
其實luogu題解裏面的那份標解已經足夠清楚了,自己寫一遍只是爲了加深理解。
題目大意:給定一棵帶邊權的樹,給定樹上的多條鏈,每條鏈有費用,求有公共邊的兩條鏈並的邊權和-費用的最大值。
這道題的一個核心思路是考慮交邊的狀態,不難發現如下兩種情況:
沒錯我盜的圖,略略略
對於沒有公共Lca的鏈,他們的鏈交是一條直上直下的鏈
對於有公共Lca的鏈,他們的鏈交一條沒有任何特殊性質的鏈
對於前者,其特殊性在於鏈交,對於後者,公共的Lca本身就是很特殊的性質。
分情況討論這兩種情況
Case1
不難發現,我們可以將每條鏈都拆成直上直下的兩條鏈。我們把一條鏈的邊權值加到另一條鏈上,這樣我們可以把問題直接轉化成所有鏈都是直上直下的情況。
一般統計這類問題,都要找基準點。考慮將紅點,也就是交點的下端作爲基準點統計答案。
考慮枚舉所有的紅點。那麼對於每個紅點,答案就是
其中是兩條鏈的下端點,是花費,表示的帶權深度。
對於某個紅點,是常數。
考慮對於紅點的每個子樹開一棵線段樹,每條鏈以上端點深度爲下標,考慮啓發是合併。合併的時候,考慮某個線段樹節點的左右子樹,右子樹的肯定大於左子樹的,那麼答案就是
線段樹中維護的最大值即可。
Case2
稍微麻煩了一些。我們先考慮所有Lca都在同一個點上的情況。
我們仍然考慮將兩個藍點的答案統計在紅點上。
其中表示兩個藍點對應綠點的帶權距離。
一個非常神仙的做法是,新建兩個節點與連邊,邊權就是和
就是求的最大值。
這是一個最遠點對問題。
我們考慮維護紅點的不同子樹內藍點對應的綠點集合的最遠點對。
將所有子樹合併起來即可得到紅點的最遠點對。合併的時候統計答案。
因爲將兩個最遠點對集合合並可以兩兩匹配比較做。
當不在同一個點上的時候,暴力記錄一下藍點所在的位置,對每個點跑個虛樹即可。
總複雜度
本題的破題關鍵在於:
1.信息的形式化表達。
採用紅點作爲基準點用盡量少的變量轉化原問題。
2.變量的分離處理和維護。
將不同的變量的信息分離並維護,把複雜度從降到
代碼
當然還考察了選手的代碼功底和數據結構功底啦!
#include<bits/stdc++.h>
const int N = 1e5 + 10;
typedef long long LL;
const LL oo = 1e17;
LL ri() {
char c = getchar(); LL 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;
}
template<typename T>void cmax(T &a, T b) {a = std::max(a, b);}
int d[N], ps[N], Lg[N], de[N], sz[N], ds[N], fa[N], pr[N], nx[N], to[N], bin[20], mn[20][N], tot, tp, n;
LL D[N], ans;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
int Be(int u, int v) {return de[u] < de[v] ? u : v;}
int Lca(int u, int v) {
u = ps[u]; v = ps[v];
if(u > v)
std::swap(u, v);
int t = Lg[v - u + 1];
return Be(mn[t][u], mn[t][v - bin[t] + 1]);
}
LL Dis(int u, int v) {
if(!u || !v) return -oo;
return D[u] + D[v] - (D[Lca(u, v)] << 1);
}
void Dfs(int u) {
de[u] = de[fa[u]] + 1; D[u] += D[fa[u]];
mn[0][++tot] = u; ps[u] = tot; sz[u] = 1; ds[u] = 0;
for(int i = pr[u]; i; i = nx[i]) {
Dfs(to[i]); mn[0][++tot] = u;
sz[u] += sz[to[i]];
if(sz[to[i]] > sz[ds[u]])
ds[u] = to[i];
}
}
void Pre() {
Dfs(1); bin[0] = 1;
for(int i = 1;(bin[i] = bin[i - 1] << 1) <= tot; ++i)
for(int j = 1;j + bin[i] - 1 <= tot; ++j)
mn[i][j] = Be(mn[i - 1][j], mn[i - 1][j + bin[i - 1]]);
Lg[0] = -1;
for(int i = 1;i <= tot; ++i)
Lg[i] = Lg[i >> 1] + 1;
d[1] = 1;
for(int i = 2;i <= n; ++i)
d[i] = ds[fa[i]] == i ? d[fa[i]] : i;
}
int Jump(int u, int c) {
for(;d[u] != d[c]; u = fa[d[u]])
if(fa[d[u]] == c)
return d[u];
return ds[c];
}
namespace Diff {
const int T = 2e6 + 10;
LL mx0[T], mx1[T], w0, w1;
int rt[N], ls[T], rs[T], sz, A;
int Newnode() {
++sz;
mx0[sz] = mx1[sz] = -oo;
ls[sz] = rs[sz] = 0;
return sz;
}
void Ins(int &p, int L, int R, int x) {
if(!p) p = Newnode();
cmax(mx0[p], w0); cmax(mx1[p], w1);
if(L == R) return ; int m = L + R >> 1;
x <= m ? Ins(ls[p], L, m, x) : Ins(rs[p], m + 1, R, x);
cmax(ans, mx0[ls[p]] + mx1[rs[p]] - D[A]);
}
void Up(int p) {
mx0[p] = std::max(mx0[ls[p]], mx0[rs[p]]);
mx1[p] = std::max(mx1[ls[p]], mx1[rs[p]]);
}
void Del(int &p, int L, int R, int x) {
if(L == x) return p = 0, void();
int m = L + R >> 1;
x <= m ? rs[p] = 0, Del(ls[p], L, m, x) : Del(rs[p], m + 1, R, x);
Up(p);
}
int Merge(int u, int v, int L, int R) {
if(!u || !v) return u | v;
if(L == R) {
cmax(mx0[u], mx0[v]);
cmax(mx1[u], mx1[v]);
return u;
}
cmax(ans, mx0[ls[u]] + mx1[rs[v]] - D[A]);
cmax(ans, mx0[ls[v]] + mx1[rs[u]] - D[A]);
int m = L + R >> 1;
ls[u] = Merge(ls[u], ls[v], L, m);
rs[u] = Merge(rs[u], rs[v], m + 1, R);
return Up(u), u;
}
void Ins(int u, int c, LL w) {
w0 = w; w1 = w + D[c]; A = u;
Ins(rt[u], 1, n, de[c]);
}
void Restore() {
sz = 0;
for(int i = 1;i <= n; ++i)
rt[i] = 0;
}
void Work() {
for(int i = n; i > 2; --i) {
Del(rt[i], 1, n, de[i] - 1); A = fa[i];
rt[fa[i]] = Merge(rt[fa[i]], rt[i], 1, n);
}
Restore();
}
}
namespace Same {
struct Node {
int u; LL w;
Node(int _u = 0, LL _w = 0) : u(_u), w(_w) {}
};
LL Dis(Node u, Node v) {return ::Dis(u.u, v.u) + u.w + v.w;}
struct Data {
int u; Node v;
Data(int _u = 0, Node _v = Node(0, 0)) : u(_u), v(_v) {}
};
std::vector<Data>vec[N];
bool cmp(Data a, Data b) {return ps[a.u] < ps[b.u];}
struct MaxDis {
Node u, v;
MaxDis(Node _u = Node(), Node _v = Node()) : u(_u), v(_v) {}
bool operator < (MaxDis a) const {return Dis(u, v) < Dis(a.u, a.v);}
void Union(MaxDis &a, int c) {
if(!u.u)
return *this = a, a = MaxDis(), void();
MaxDis x(u, a.u);
cmax(x, MaxDis(u, a.v));
cmax(x, MaxDis(v, a.u));
cmax(x, MaxDis(v, a.v));
cmax(ans, (Dis(x.u, x.v) >> 1) - D[c]);
cmax(x, a); cmax(*this, x);
a = MaxDis();
}
}x[N];
int h[N];
void Ins(int x, int u, int v, LL w) {vec[x].push_back(Data(u, Node(v, w)));}
void Up(int f, int u) {x[f].Union(x[u], f);}
void Ins(int u) {
if(h[tp] == u)
return ;
int c = Lca(h[tp], u);
for(;tp > 1 && de[h[tp - 1]] >= de[c]; --tp)
Up(h[tp - 1], h[tp]);
if(h[tp] != c)
Up(c, h[tp]), h[tp] = c;
h[++tp] = u;
}
void Work() {
for(int i = 2;i <= n; ++i)
if(vec[i].size()) {
std::sort(vec[i].begin(), vec[i].end(), cmp);
h[tp = 1] = i;
for(Data j : vec[i]) {
MaxDis Ad(j.v);
x[j.u].Union(Ad, j.u);
Ins(j.u);
}
for(;tp > 1; --tp)
Up(h[tp - 1], h[tp]);
if(tp)
x[h[1]] = MaxDis();
vec[i].clear();
}
}
}
int main() {
Diff::mx0[0] = Diff::mx1[0] = -oo;
for(int T = ri();T--;) {
n = ri(); ans = -oo;
for(int i = 1;i < n; ++i) {
int u = ri(), v = ri(), w = ri();
add(u, v); D[v] = w; fa[v] = u;
}
Pre();
for(int m = ri(); m--;) {
int u = ri(), v = ri(); LL w = ri();
int c = Lca(u, v); LL len = Dis(u, v);
if(u != c) {
Diff::Ins(u, c, len - w);
Same::Ins(Jump(u, c), u, v, len + D[u] - (w << 1));
}
if(v != c) {
Diff::Ins(v, c, len - w);
Same::Ins(Jump(v, c), v, u, len + D[v] - (w << 1));
}
}
Diff::Work();
Same::Work();
if(ans < -1e15)
puts("F");
else
printf("%lld\n", ans);
tot = tp = 0;
for(int i = 1;i <= n; ++i)
pr[i] = 0;
}
return 0;
}