temporaryDO 是一個很菜的 OIer 。在 4 月,他在省隊選拔賽的考場上見到了《林克卡特樹》一題,其中 \(k = 0\) 的部分分是求樹 \(T\) 上的最長鏈。可憐的 temporaryDO 並不會作這道題,他在考場上抓貓耳撓貓腮都想不出一點思路。node
這時,善良的闆闆出如今了空中,他的身上發出璀璨卻柔和的光芒,盪漾在考場上。‘‘題目並不難。’’ 闆闆說。那充滿磁性的聲音,讓 temporaryDO 全身充滿了力量。c++
他決定:寫一個枚舉點對求 LCA 算距離的 \(k = 0\) 的 \(O(n^2\log\ n)\) 的部分分程序!因而, temporaryDO 選擇以 \(1\) 爲根,創建了求 LCA 的樹鏈剖分結構,而後寫了二重 for 循環枚舉點對。數組
然而,菜菜的 temporaryDO 不當心開小了數組,因而數組越界到了一片神祕的內存區域。但剛好的是,那片內存區域存儲的區域剛好是另外一棵樹 \(T′\) 。這樣一來,程序並無 RE ,但他求 \(x\) 和 \(y\) 的距離的時候,計算的是ui
\[depth(x) + depth(y) - (depth(LCA(x , y)) + depth′ (LCA′ (x, y)))\]spa
最後程序會輸出每一對點對 \(i, j (i \le j)\) 的如上定義的‘‘距離’’ 的最大值。code
temporaryDO 的程序在評測時光榮地爆零了。但他並不服氣,他決定花好幾天把本身的程序跑出來。請你根據 \(T\) 和 \(T′\) 幫幫可憐的 temporaryDO 求出他程序的輸出。內存
第一行包含一個整數 \(n\) ,表示樹上的節點個數;get
第 \(2\) 到第 \(n\) 行,每行三個整數 \(x , y , v\) ,表示 \(T\) 中存在一條從 \(x\) 到 \(y\) 的邊,其長度爲 \(v\) ;it
第 \(n + 1\) 到第 \(2n - 1 行\) ,每行三個整數 \(x , y , v\) ,表示 \(T′\) 中存在一條從 \(x\) 到 \(y\) 的邊,其長度爲 \(v\) 。class
輸出一行一個整數,表示 temporaryDO 的程序的輸出。
對於全部數據, \(n \le 366666 , |v| \le 2017011328\) 。
之前一直以爲邊分治和點分治沒什麼區別,作了這道題才發現我太naive了。
首先題目中給的式子,很差看,因此咱們把它變一下形:
\[ \begin{align} &dep_x+dep_y-dep_{lca(x,y)} \\&=\frac{1}{2}(2*dep_x+dep_y-2*dep_{lca(x,y)}) \\&=\frac{1}{2}(dep_x+dep_y+dis_{x,y}) \end{align} \]
\[ ans=\frac{1}{2}(dep_x+dep_y+dis_{x,y}-2*dep'_{lca'(x,y)}) \]
因而咱們能夠枚舉第二顆樹的\(lca\),而後計算其子樹之間的\(dep_x+dep_y+dis_{x,y}\)的最大值。後面部分就能夠用邊分治來維護。
能夠類比點分治來理解邊分治,就是在每一個分治連通塊中找到一條邊使得邊兩端的連通塊大小盡可能平均。可是咱們發現,一個菊花就能夠把這個分治卡死。緣由是某個點的度數太大了。因而咱們考慮轉成二叉樹。具體就是每一個點的上面連一個額外點,而後一個父親節點連向其中一個兒子的額外點,幾個兒子的額外點再連成一條線(代碼一看就懂)。
假設分治中心邊是\((x,y)\),咱們像點分治那樣統計分支塊內每一個點\(p\)到分治中心邊的其中一個點的距離(\(dep_p+dis_{x,p}\))。這個距離有兩種方向,分別對應\(x,y\)所在的連通塊。咱們發現,邊分治的分治樹是棵二叉樹。因此對於每一個點,咱們能夠開個二叉樹,記錄其在每個分治連通塊內的到中心點的距離。
而後就能夠算答案了。當枚舉了第二棵樹上的\(lca\)的時候,合併每一個子樹的二叉樹,同一種節點(到根路徑相同)表明同一個分支連通塊,咱們能夠在合併過程當中更新答案。
代碼:
#include<bits/stdc++.h> #define ll long long #define N 800005 using namespace std; inline ll Get() {ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int n; struct graph { int to[N<<2],nxt[N<<2],dis[N<<2]; int h[N<<1],cnt=1; void add(int i,int j,int d) { to[++cnt]=j; nxt[cnt]=h[i]; dis[cnt]=d; h[i]=cnt; } void Init() { memset(h,0,sizeof(h)); cnt=1; } }s,g; int vertex; ll dep[N]; void build_edge(int v,int fr) { int lst=v; for(int i=s.h[v];i;i=s.nxt[i]) { int to=s.to[i]; if(to==fr) continue ; vertex++; g.add(lst,vertex,0); g.add(vertex,lst,0); g.add(vertex,to,s.dis[i]); g.add(to,vertex,s.dis[i]); lst=vertex; dep[to]=dep[v]+s.dis[i]; build_edge(to,v); } } int size[N<<1]; int sum,E,mx; bool vis[N<<1]; void Find_edge(int v,int fr) { size[v]=1; for(int i=g.h[v];i;i=g.nxt[i]) { int to=g.to[i]; if(vis[i]||to==fr) continue ; Find_edge(to,v); size[v]+=size[to]; int now=max(size[to],sum-size[to]); if(mx>now) { mx=now; E=i; } } } struct node { ll dis; int dir; node() {} node(ll _dis,int _dir) {dis=_dis,dir=_dir;} }; vector<node>st[N<<1]; void statis(int v,int fr,ll dis,int dir) { size[v]=1; if(v<=n) { st[v].push_back(node(dep[v]+dis,dir)); } for(int i=g.h[v];i;i=g.nxt[i]) { int to=g.to[i]; if(to==fr||vis[i]) continue ; statis(to,v,dis+g.dis[i],dir); size[v]+=size[to]; } } void solve(int v) { sum=size[v]; mx=1e9; Find_edge(v,0); int x=g.to[E],y=g.to[E^1]; vis[E]=vis[E^1]=1; statis(x,0,0,0),statis(y,0,g.dis[E],1); if(size[x]>1) solve(x); if(size[y]>1) solve(y); } int rt[N<<1]; int ls[N*20],rs[N*20]; ll lmx[N*20],rmx[N*20]; int tot; void build_tree(int &v,vector<node>&a,int now) { if(now==a.size()) return ; v=++tot; if(a[now].dir==0) { lmx[v]=a[now].dis; rmx[v]=-1ll<<60; build_tree(ls[v],a,now+1); } else { rmx[v]=a[now].dis; lmx[v]=-1ll<<60; build_tree(rs[v],a,now+1); } } ll ans=-1ll<<60; ll Dis; int Merge(int a,int b) { if(!a||!b) return a+b; ans=max(ans,max(lmx[a]+rmx[b],lmx[b]+rmx[a])-2*Dis); lmx[a]=max(lmx[a],lmx[b]); rmx[a]=max(rmx[a],rmx[b]); ls[a]=Merge(ls[a],ls[b]); rs[a]=Merge(rs[a],rs[b]); return a; } void dfs2(int v,int fr,ll dis) { ans=max(ans,2*dep[v]-2*dis); for(int i=s.h[v];i;i=s.nxt[i]) { int to=s.to[i]; if(to==fr) continue ; dfs2(to,v,dis+s.dis[i]); Dis=dis; rt[v]=Merge(rt[v],rt[to]); } } int main() { n=Get(); for(int i=1;i<n;i++) { int a=Get(),b=Get(),c=Get(); s.add(a,b,c),s.add(b,a,c); } vertex=n; build_edge(1,0); size[1]=vertex; solve(1); for(int i=1;i<=n;i++) { build_tree(rt[i],st[i],0); } s.Init(); for(int i=1;i<n;i++) { int a=Get(),b=Get(),c=Get(); s.add(a,b,c),s.add(b,a,c); } dfs2(1,0,0); cout<<ans/2; return 0; }