【学习笔记】[CTSC2018]暴力写挂 (边分树合并/边分治+虚树)

显然我们可以将问题转化为 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))) 21(dep(x)+dep(y)+dist(x,y)2dep(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)2dep(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(nlogn)。

算法二
类比点分树,我们可以构造出边分树,其中叶子节点都是原树上的点,非叶子节点是原树上的边。类比 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(nlogn)

我信仰什么,我便实现哪种方法。

因为太懒了所以只写了虚树的做法。

#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__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530068.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(12)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示