神題一道
題意:
一棵樹,有邊權, 條路徑,路徑有代價,選擇兩條相交的路徑使得路徑的並的邊權 - 總代價最大
直接考慮 的性質
:
路徑的 兩兩不同,於是路徑的交只有可能是直上直下
我們枚舉紅點,那麼這個時候的貢獻應該是
考慮樹上差分後線段樹合併,合併的時候考慮兩個不同子樹路徑的拼接
現在的問題就是處理哪個在上面有點煩
發現可以按深度維護線段樹,維護 和 的最大值
線段樹合併的時候考慮一個的左子樹有一個右子樹拼接,就可以很巧妙地知道誰在上方
複雜度
最後的貢獻是:
我們枚舉 發現除 外 獨立
如果我們令 的點權爲 的話,那麼問題就是找一對最遠點
現在的問題就是支持集合合併,合併的時候詢問最遠點
合併後的最遠點一定是兩個集合四個最遠點的其中兩個,於是可以很方便維護
對於所有路徑的 不在一個點時,把路徑存在 處然後大力建虛樹就可以了
複雜度
兩種方法都有很巧妙的地方
第一種是考慮線段樹合併的時候更新答案就可以知道誰的深度更深
第二種是將問題轉換爲最遠點對,對於一個點集的最遠點對只需要維護兩個點並支持快速合併
#include<bits/stdc++.h>
#define cs const
#define mp make_pair
using namespace std;
typedef long long ll;
cs ll INF = 1e18;
void Mx(ll &a, ll b){ if(a < b) a = b; }
int read(){
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
return cnt * f;
}
cs int N = 1e5 + 50;
int T, n, m; ll ans = -INF;
int fi[N], nxt[N], to[N], w[N], tot;
void add(int x, int y, int z){ nxt[++tot] = fi[x], fi[x] = tot, to[tot] = y, w[tot] = z; }
int lg[N], st[N][20], in[N], sgn, dep[N]; ll d[N];
struct cp{ int u; ll w; cp(int _u = 0, ll _w = 0){u = _u, w = _w; } };
void dfs(int u, int fa){
st[in[u] = ++sgn][0] = u;
for(int i = fi[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa) continue;
dep[t] = dep[u] + 1; d[t] = d[u] + (ll)w[i];
dfs(t, u); st[++sgn][0] = u;
}
}
int ck(int x, int y){ return dep[x] < dep[y] ? x : y; }
int lca(int x, int y){
int l = in[x], r = in[y];
if(l > r) swap(l, r);
int d = lg[r - l + 1];
return ck(st[l][d], st[r-(1<<d)+1][d]);
}
ll dist(int x, int y){ return d[x] + d[y] - 2 * d[lca(x, y)]; }
namespace S1{
cs int N = ::N * 25;
ll mx1[N], mx2[N], delta;
int ls[N], rs[N], rt[::N], nd;
vector<cp> G[N];
#define mid ((l+r)>>1)
void pushup(int x){
mx1[x] = max(mx1[ls[x]], mx1[rs[x]]);
mx2[x] = max(mx2[ls[x]], mx2[rs[x]]);
}
void ins(int &x, int l, int r, int p, ll v1, ll v2){
if(!x){
x = ++nd; ls[x] = rs[x] = 0;
mx1[x] = mx2[x] = -INF;
}
if(l == r){ Mx(mx1[x], v1); Mx(mx2[x], v2); return; }
if(p <= mid) ins(ls[x],l,mid,p,v1,v2);
else ins(rs[x],mid+1,r,p,v1,v2); pushup(x);
}
void dec(int &x, int l, int r, int p){
if(!x) return;
if(l == r){ x = 0; return; }
if(p <= mid) dec(ls[x],l,mid,p);
else dec(rs[x],mid+1,r,p);
pushup(x);
}
int merge(int &x, int y, int l = 1, int r = n){
if(!x||!y) return x|y;
if(l == r){ Mx(mx1[x],mx1[y]); Mx(mx2[x],mx2[y]); return x; }
ans = max(ans, mx1[ls[x]] + mx2[rs[y]] - delta);
ans = max(ans, mx2[rs[x]] + mx1[ls[y]] - delta);
ls[x] = merge(ls[x], ls[y], l, mid);
rs[x] = merge(rs[x], rs[y], mid+1, r); pushup(x); return x;
}
void dfs(int u, int fa){
for(int i = fi[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa) continue; dfs(t, u);
}
delta = d[u];
for(int i = fi[u]; i; i = nxt[i]){
int t = to[i]; if(t == fa) continue;
dec(rt[t], 1, n, dep[u]);
rt[u] = merge(rt[u], rt[t]);
}
for(cp v:G[u]){
int r = 0; ins(r, 1, n, dep[v.u], v.w, v.w + d[v.u]);
rt[u] = merge(rt[u], r);
}
}
void Solve(){
mx1[0] = mx2[0] = -INF;
dfs(1, 0); nd = 0;
for(int i = 1; i <= n; i++) rt[i] = 0, G[i].clear();
}
}
namespace S2{
struct Set{
cp x, y; ll d;
bool operator < (cs Set &a) cs{ return d < a.d; }
};
Set f[N];
struct Path{ int u, v; ll w; } ;
vector<Path> G[N];
int sta[N], arr[N], top, now, rt;
bool cmp(int x, int y){ return in[x] < in[y]; }
Set calc(cp A, cp B){
ll di = dist(A.u, B.u) + A.w + B.w;
ans = max(ans, di / 2 - d[now]);
return (Set){A, B, di};
}
void merge(Set &A, Set &B){
if(A.d == -INF){ A = B; B.d = -INF; return; }
if(B.d == -INF) return;
if(now ^ rt){
Set P = max(calc(A.x,B.x),calc(A.x,B.y));
P = max(P, max(calc(A.y,B.x), calc(A.y,B.y)));
A = max(A, max(P, B));
} B.d = -INF;
}
void work(cs vector<Path> &vec){
int top = 0, sz = 0;
for(Path t:vec){
arr[++sz] = t.u;
arr[++sz] = t.v;
cp a(t.u, d[t.v] + t.w);
cp b(t.v, d[t.u] + t.w);
Set x = (Set){a, a, a.w << 1}, y = (Set){b, b, b.w << 1};
merge(f[now = t.u], y); merge(f[now = t.v], x);
}
sort(arr + 1, arr + sz + 1, cmp);
sta[++top] = arr[1];
for(int i = 2; i <= sz; i++) if(arr[i] != arr[i-1]){
int p = arr[i], l = lca(p, sta[top]);
while(top > 1 && dep[sta[top - 1]] >= dep[l])
merge(f[now = sta[top - 1]], f[sta[top]]), --top;
if(sta[top] ^ l) merge(f[now = l], f[sta[top]]), sta[top] = l;
sta[++top] = p;
} while(top > 1) merge(f[now = sta[top - 1]], f[sta[top]]), --top;
f[sta[1]].d = -INF;
}
void Solve(){
for(int i = 1; i <= n; i++) f[i].d = -INF;
for(int i = 1; i <= n; i++) if(G[i].size() > 1) rt = i, work(G[i]);
for(int i = 1; i <= n; i++) G[i].clear();
}
}
void Clear(){
ans = -INF;
memset(fi, 0, sizeof(int)*(n+1)); tot = sgn = 0;
}
void Solve(){
n = read();
for(int i = 1; i < n; i++){
int x = read(), y = read(), z = read();
add(x, y, z); add(y, x, z);
} dep[1] = 1; dfs(1, 0);
for(int i = 2; i <= sgn; i++) lg[i] = lg[i>>1] + 1;
for(int j = 1; (1<<j)<=sgn; j++)
for(int i = 1; i+(1<<j)-1 <= sgn; i++)
st[i][j] = ck(st[i][j-1], st[i+(1<<j-1)][j-1]);
m = read();
for(int i = 1; i <= m; i++){
int u = read(), v = read(); ll w; scanf("%lld", &w);
if(u == v) continue;
int l = lca(u, v); ll dis = dist(u, v);
if(v ^ l) S1::G[v].push_back(cp(l, dis - w));
if(u ^ l) S1::G[u].push_back(cp(l, dis - w));
S2::G[l].push_back((S2::Path){u, v, dis - 2 * w});
}
S1::Solve();
S2::Solve();
if(ans < -1e17) puts("F");
else cout << ans << '\n';
}
int main(){
freopen("center.in","r",stdin);
freopen("center.out","w",stdout);
T = read();
while(T--) Solve(), Clear();
return 0;
}