题目传送门
即zjoi线图之后又一道两百行代码的题。
其实luogu题解里面的那份标解已经足够清楚了,自己写一遍只是为了加深理解。
题目大意:给定一棵带边权的树,给定树上的多条链,每条链有费用,求有公共边的两条链并的边权和-费用的最大值。
这道题的一个核心思路是考虑交边的状态,不难发现如下两种情况:
没错我盗的图,略略略
对于没有公共Lca的链,他们的链交是一条直上直下的链
对于有公共Lca的链,他们的链交一条没有任何特殊性质的链
对于前者,其特殊性在于链交,对于后者,公共的Lca本身就是很特殊的性质。
分情况讨论这两种情况
不难发现,我们可以将每条链都拆成直上直下的两条链。我们把一条链的边权值加到另一条链上,这样我们可以把问题直接转化成所有链都是直上直下的情况。
一般统计这类问题,都要找基准点。考虑将红点,也就是交点的下端作为基准点统计答案。
考虑枚举所有的红点。那么对于每个红点,答案就是
L e n 1 + L e n 2 − C 1 − C 2 − D R e d + m a x ( D G r e e n , D B l u e ) Len_1+Len_2-C_1-C_2-D_{Red}+max(D_{Green},D_{Blue}) Len1+Len2−C1−C2−DRed+max(DGreen,DBlue)
其中 u , v u,v u,v是两条链的下端点, C 1 , C 2 C_1,C_2 C1,C2是花费, D x D_x Dx表示 x x x的带权深度。
对于某个红点, D R e d D_{Red} DRed是常数。
考虑对于红点的每个子树开一棵线段树,每条链以上端点深度为下标,考虑启发是合并。合并的时候,考虑某个线段树节点的左右子树,右子树的 D D D肯定大于左子树的 D D D,那么答案就是 M a x ( L e n 1 + C 1 + D 1 ) + M a x ( L e n 2 + C 2 ) − D R e d Max(Len_1+C_1+D_1)+Max(Len_2+C_2)-D_{Red} Max(Len1+C1+D1)+Max(Len2+C2)−DRed
线段树中维护 f x , 0 / 1 = L e n x + C x ( + D x ) f_{x,0/1}=Len_x+C_x(+D_x) fx,0/1=Lenx+Cx(+Dx)的最大值即可。
稍微麻烦了一些。我们先考虑所有Lca都在同一个点上的情况。
我们仍然考虑将两个蓝点 u , v u,v u,v的答案统计在红点 R e d Red Red上。
2 A n s = L e n 1 + L e n 2 + D u + D v − 2 D R e d + D i s t ( p u , p v ) 2Ans=Len_1+Len_2+D_u+D_v-2D_{Red}+Dist(p_u,p_v) 2Ans=Len1+Len2+Du+Dv−2DRed+Dist(pu,pv)
其中 D i s t ( p u , p v ) Dist(p_u,p_v) Dist(pu,pv)表示两个蓝点对应绿点的带权距离。
一个非常神仙的做法是,新建两个节点 p u ′ , p v ′ p'_u,p'_v pu′,pv′与 p u , p v p_u,p_v pu,pv连边,边权就是 L e n 1 + D u Len_1+D_u Len1+Du和 L e n 2 + D v Len_2+D_v Len2+Dv
就是求 D i s t ( p u ′ , p v ′ ) − 2 D R e d Dist(p'_u,p'_v)-2D_{Red} Dist(pu′,pv′)−2DRed的最大值。
这是一个最远点对问题。
我们考虑维护红点的不同子树内蓝点对应的绿点集合的最远点对。
将所有子树合并起来即可得到红点的最远点对。合并的时候统计答案。
因为将两个最远点对集合合并可以 O ( 6 ) O(6) O(6)两两匹配比较做。
当 L c a Lca Lca不在同一个点上的时候,暴力记录一下蓝点所在的位置,对每个点跑个虚树即可。
总复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
本题的破题关键在于:
1.信息的形式化表达。
采用红点作为基准点用尽量少的变量转化原问题。
2.变量的分离处理和维护。
将不同的变量的信息分离并维护,把复杂度从 O ( n 2 ) O(n^2) O(n2)降到 O ( n l o g ) O(nlog) O(nlog)
当然还考察了选手的代码功底和数据结构功底啦!
#include
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;
}