显然我们可以将问题转化为
1
2
(
dep
(
x
)
+
dep
(
y
)
+
dist
(
x
,
y
)
−
2
dep
′
(
lca
′
(
x
,
y
)
)
)
\frac{1}{2}(\text{dep}(x)+\text{dep}(y)+\text{dist}(x,y)-2\text{dep}'(\text{lca}'(x,y)))
2 1 ( dep ( x ) + dep ( y ) + dist ( x , y ) − 2 dep ′ ( lca ′ ( x , y ))) 。因为要分治所以这里写成两点距离比较方便。
如果边权非负的话直接套用 [WC2018]通道 的做法合并最远点对即可,然而这题边权有负。
考虑两种算法。
算法一 边分治+虚树。
对第一颗树进行边分治,设
x
x
x 到分治中心的距离是
D
(
x
)
D(x)
D ( x ) ,显然答案可以写成
dep
(
x
)
+
D
(
x
)
+
dep
(
y
)
+
D
(
y
)
−
2
dep
′
(
lca
′
(
x
,
y
)
)
\text{dep}(x)+D(x)+\text{dep}(y)+D(y)-2\text{dep}'(\text{lca}'(x,y))
dep ( x ) + D ( x ) + dep ( y ) + D ( y ) − 2 dep ′ ( lca ′ ( x , y )) ,其中
x
x
x ,
y
y
y 属于分治中心的两侧。那么直接在第二颗树对应的虚树上统计即可。
如果加上归并排序和
R
M
Q
RMQ
RMQ 求
lca
\text{lca}
lca 的优化可以做到
O
(
n
log
n
O(n\log n
O ( n log n )。
算法二 类比点分树,我们可以构造出边分树,其中叶子节点都是原树上的点,非叶子节点是原树上的边。类比
trie
\text{trie}
trie 树,我们可以得到一个叶子节点在每次分治过程中被分到了哪一边。那么我们可以对边分树上的叶子节点到根的链进行合并,道理很简单,如果
x
x
x ,
y
y
y 到根节点的路径上有公共点
z
z
z ,那么可以在
z
z
z 处统计答案。那么我们只需在第二棵树上枚举
lca
′
(
x
,
y
)
\text{lca}'(x,y)
lca ′ ( x , y ) ,把儿子节点全部合并起来时统计答案即可。
复杂度
O
(
n
log
n
)
O(n\log n)
O ( n log n ) 。
我信仰什么,我便实现哪种方法。
因为太懒了所以只写了虚树的做法。
#include <bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define db double
#define pb push_back
#define inf 0x3f3f3f3f3f3f3f3f
#define int ll
using namespace std;
const int N=2e6 +5 ;
int n,n2,num,dfn[N];
int hd[N],to[N],st[N],nxt[N],vis[N],w[N],tot=1 ;
int f2[N][20 ],dep4[N];
ll dep[N],dep1[N],dep2[N],dep3[N];
vector<pair<int ,int >>g[N],g3[N];
void add (int x,int y,int z) {
to[++tot]=y,st[tot]=x,w[tot]=z,nxt[tot]=hd[x],hd[x]=tot;
to[++tot]=x,st[tot]=y,w[tot]=z,nxt[tot]=hd[y],hd[y]=tot;
}
int Lca2 (int x,int y) {
if (dep4[x]<dep4[y])swap (x,y);
for (int i=19 ;i>=0 ;i--)if (dep4[f2[x][i]]>=dep4[y])x=f2[x][i];
if (x==y)return x;
for (int i=19 ;i>=0 ;i--)if (f2[x][i]!=f2[y][i])x=f2[x][i],y=f2[y][i];
return f2[x][0 ];
}
void dfs (int u,int topf) {
f2[u][0 ]=topf,dep4[u]=dep4[topf]+1 ;
for (int i=1 ;i<20 ;i++)f2[u][i]=f2[f2[u][i-1 ]][i-1 ];
int lst=u;
for (auto v:g[u]){
if (v.fi!=topf){
++n2,add (lst,n2,0 ),add (n2,v.fi,v.se),lst=n2,dep1[v.fi]=dep1[u]+v.se,dfs (v.fi,u);
}
}
}
int edge,D,siz[N],c[N],s[N],cnt,f[N][20 ];
ll res (-inf) ;
vector<int >vec;
void dfs2 (int u,int topf,int sz) {
siz[u]=1 ;
for (int k=hd[u];k;k=nxt[k]){
int v=to[k];
if (!vis[k]&&v!=topf){
dfs2 (v,u,sz),siz[u]+=siz[v];
if (max (siz[v],sz-siz[v])<D)D=max (siz[v],sz-siz[v]),edge=k;
}
}
}
ll dp1[N],dp2[N];
int p[N],m;
vector<int >g2[N];
void dfs3 (int u,int topf,int C) {
c[u]=C;if (u<=n)p[++m]=u;
for (int k=hd[u];k;k=nxt[k]){
int v=to[k];
if (!vis[k]&&v!=topf)dep2[v]=dep2[u]+w[k],dfs3 (v,u,C);
}
}
void Add (int x,int y) {
if (dep[x]>dep[y])swap (x,y);
g2[x].pb (y);
}
bool cmp (int x,int y) {
return dfn[x]<dfn[y];
}
void dfs5 (int u,int topf) {
dfn[u]=++num,dep[u]=dep[topf]+1 ,f[u][0 ]=topf;
for (int i=1 ;i<20 ;i++)f[u][i]=f[f[u][i-1 ]][i-1 ];
for (auto v:g3[u]){
if (v.fi!=topf)dep3[v.fi]=dep3[u]+v.se,dfs5 (v.fi,u);
}
}
int Lca (int x,int y) {
if (dep[x]<dep[y])swap (x,y);
for (int i=19 ;i>=0 ;i--)if (dep[f[x][i]]>=dep[y])x=f[x][i];
if (x==y)return x;
for (int i=19 ;i>=0 ;i--)if (f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0 ];
}
void build () {
for (int i=1 ;i<=m;i++)vec.pb (p[i]);
sort (p+1 ,p+1 +m,cmp);
s[cnt=1 ]=1 ,vec.pb (1 );
for (int i=1 ;i<=m;i++){
if (p[i]==1 )continue ;
int u=p[i],v=Lca (u,s[cnt]);
if (v==s[cnt])s[++cnt]=u;
else {
int l=0 ;
while (dep[s[cnt]]>dep[v]){
if (l)Add (l,s[cnt]);
l=s[cnt--];
}
if (s[cnt]==v){
Add (l,v),s[++cnt]=u;
}
else {
Add (l,v),s[++cnt]=v,vec.pb (v),s[++cnt]=u;
}
}
}for (int i=1 ;i<cnt;i++)Add (s[i],s[i+1 ]);
}
void dfs4 (int u) {
dp1[u]=dp2[u]=-inf;
if (c[u]==1 )dp1[u]=dep1[u]+dep2[u];
if (c[u]==2 )dp2[u]=dep1[u]+dep2[u];
for (auto v:g2[u]){
dfs4 (v),res=max (res,w[edge]+max (dp1[u]+dp2[v],dp2[u]+dp1[v])-2 *dep3[u]);
dp1[u]=max (dp1[u],dp1[v]),dp2[u]=max (dp2[u],dp2[v]);
}
}
void solve (int u,int sz) {
if (sz==1 )return ;
edge=-1 ,D=0x3f3f3f3f ,dfs2 (u,0 ,sz);
vis[edge]=vis[edge^1 ]=1 ,m=0 ,dep2[st[edge]]=dep2[to[edge]]=0 ,dfs3 (st[edge],0 ,1 ),dfs3 (to[edge],0 ,2 );
build (),dfs4 (1 );
for (int i=0 ;i<vec.size ();i++)g2[vec[i]].clear (),c[vec[i]]=0 ;vec.clear ();
int tmp=edge;
solve (st[tmp],sz-siz[to[tmp]]),solve (to[tmp],siz[to[tmp]]);
}
signed main () {
ios::sync_with_stdio (false );
cin.tie (0 ),cout.tie (0 );
cin>>n,n2=n;
for (int i=1 ;i<n;i++){
int x,y,z;cin>>x>>y>>z;
g[x].pb ({y,z}),g[y].pb ({x,z});
}
for (int i=1 ;i<n;i++){
int x,y,z;cin>>x>>y>>z;
g3[x].pb ({y,z}),g3[y].pb ({x,z});
}
dfs (1 ,0 ),dfs5 (1 ,0 );
for (int i=1 ;i<=n;i++)res=max (res,2 *(dep1[i]-dep3[i]));
solve (1 ,n2);
cout<<res/2 ;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」