BZOJ 5341: [Ctsc2018]暴力写挂 边分治+虚树
和【洛谷5115】挺像的.
都是统计信息的时候是包含两个树的,那就在一个树上边分治,另一个树上跑一个虚树dp就好了.
式子这么拆:
$dep[i]+dep[j]-(dep[LCA(i,j)]+dep'[LCA'(i,j)]$
$\Rightarrow dep[i]+dep[j]-\frac{1}{2}(dep[i]+dep[j]-dis(i,j)]-dep'[LCA'(i,j)])$
$\Rightarrow \frac{1}{2}(dep[i]+dep[j]+dis(i,j)-2dep'[LCA'(i,j)])$
其中 $dis(i,j)$ 可以拆成 $dis(i,u)+dis(j,v)+val(u,v)$,然后这个就用边分治来跑,把关键点扔到虚树上就行了 .
code:
#include <cstdio> #include <string> #include <algorithm> #include <cstring> #include <vector> #define N 1000002 #define ll long long #define inf 0x3f3f3f3f using namespace std; int n; ll ans=-inf,W; namespace IO { void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),"r",stdin); } }; namespace tree2 { vector<int>G[N],clr; ll dis[N]; int edges,t,sta,tot; int hd[N],to[N<<1],nex[N<<1],dep[N]; ll val[N<<1]; int size[N],son[N],top[N],fa[N],dfn[N],re[N]; int S[N],col[N]; ll value[N],max1[N],max2[N]; void add(int u,ll v,int c) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; } bool cmp(int a,int b) { return dfn[a]<dfn[b]; } void add_vir(int u,int v) { G[u].push_back(v); } void dfs1(int u,int ff) { fa[u]=ff,size[u]=1,dfn[u]=++t; for(int i=hd[u];i;i=nex[i]) { int v=to[i]; if(v==ff) continue; dep[v]=dep[u]+1; dis[v]=dis[u]+val[i]; dfs1(v,u); size[u]+=size[v]; if(size[v]>size[son[u]]) son[u]=v; } } void dfs2(int u,int tp) { top[u]=tp; if(son[u]) dfs2(son[u],tp); for(int i=hd[u];i;i=nex[i]) if(to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i]); } int LCA(int x,int y) { while(top[x]!=top[y]) { dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]; } return dep[x]<dep[y]?x:y; } void newnode(int x,ll v,int c) { re[++tot]=x; col[x]=c; value[x]=v; } void Insert(int x) { if(sta<=1) { S[++sta]=x; return; } int lca=LCA(x,S[sta]); if(S[sta]==lca) S[++sta]=x; else { while(sta>1&&dep[S[sta-1]]>=dep[lca]) add_vir(S[sta-1],S[sta]),--sta; if(S[sta]==lca) S[++sta]=x; else { add_vir(lca,S[sta]); S[sta]=lca; S[++sta]=x; } } } void DP(int x) { clr.push_back(x); max1[x]=max2[x]=-100000000000000000; for(int i=0;i<G[x].size();++i) { int y=G[x][i]; DP(y); ans=max(ans,max1[x]+max2[y]+W-dis[x]*2); ans=max(ans,max2[x]+max1[y]+W-dis[x]*2); max1[x]=max(max1[x],max1[y]); max2[x]=max(max2[x],max2[y]); } if(col[x]==1) ans=max(ans,value[x]+max2[x]+W-dis[x]*2),max1[x]=max(max1[x],value[x]); if(col[x]==2) ans=max(ans,value[x]+max1[x]+W-dis[x]*2),max2[x]=max(max2[x],value[x]); } void solve() { sort(re+1,re+1+tot,cmp); if(re[1]!=1) S[++sta]=1; for(int i=1;i<=tot;++i) Insert(re[i]); while(sta>1) add_vir(S[sta-1],S[sta]),--sta; DP(1); for(int i=0;i<clr.size();++i) { int x=clr[i]; max1[x]=max2[x]=value[x]=col[x]=0; G[x].clear(); } clr.clear(); tot=sta=0; } void Initialize() { dfs1(1,0); dfs2(1,1); } }; namespace tree1 { int tot,totsize,rt1,rt2,ed,mx; int edges1=1,edges2=1; int hd[N],pre[N<<1],vis[N<<2],size[N<<1]; ll dis[N<<1]; struct Edge { int to; ll w; int nex; }e[N<<2],edge[N<<2]; void add(int u,ll v,int c) { e[++edges1].nex=pre[u],pre[u]=edges1,e[edges1].to=v,e[edges1].w=c; } void add_div(int u,ll v,int c) { edge[++edges2].nex=hd[u],hd[u]=edges2,edge[edges2].to=v,edge[edges2].w=c; } void getdis(int u,int ff) { for(int i=pre[u];i;i=e[i].nex) { int v=e[i].to; if(v==ff) continue; dis[v]=dis[u]+e[i].w; getdis(v,u); } } void Build_Tree(int u,int fa) { int ff=0; for(int i=pre[u];i;i=e[i].nex) { int v=e[i].to; if(v==fa) continue; if(!ff) { ff=u; add_div(u,v,e[i].w); add_div(v,u,e[i].w); } else { ++tot; add_div(ff,tot,0); add_div(tot,ff,0); add_div(tot,v,e[i].w); add_div(v,tot,e[i].w); ff=tot; } Build_Tree(v,u); } } void find_edge(int x,int fa) { size[x]=1; for(int i=hd[x];i;i=edge[i].nex) { int y=edge[i].to; if(y==fa||vis[i]) continue; find_edge(y,x); int now=max(size[y],totsize-size[y]); if(now<mx) { mx=now; rt1=y; rt2=x; ed=i; } size[x]+=size[y]; } } void dfs(int u,int ff,ll d,int ty) { if(u<=n) { tree2::newnode(u,dis[u]+d,ty); } for(int i=hd[u];i;i=edge[i].nex) { int v=edge[i].to; if(v==ff||vis[i]) continue; dfs(v,u,d+edge[i].w,ty); } } void Divide_And_conquer(int x) { if(totsize==1) return; rt1=rt2=ed=0,mx=inf; find_edge(x,0); vis[ed]=vis[ed^1]=1; W=edge[ed].w; dfs(rt1,0,0,1); dfs(rt2,0,0,2); tree2::solve(); int sizert1=size[rt1],sizert2=totsize-size[rt1]; int tmprt1=rt1,tmprt2=rt2; totsize=sizert1; Divide_And_conquer(tmprt1); totsize=sizert2; Divide_And_conquer(tmprt2); } }; int main() { // IO::setIO("input"); int i,j; scanf("%d",&n); for(i=1;i<n;++i) { int x,y,z; scanf("%d%d%d",&x,&y,&z); tree1::add(x,y,(ll)z); tree1::add(y,x,(ll)z); } for(i=1;i<n;++i) { int x,y,z; scanf("%d%d%d",&x,&y,&z); tree2::add(x,y,(ll)z); tree2::add(y,x,(ll)z); } tree2::Initialize(); tree1::tot=n; tree1::getdis(1,0); tree1::Build_Tree(1,0); tree1::totsize=tree1::tot; tree1::Divide_And_conquer(1); ans>>=1; for(i=1;i<=n;++i) ans=max(ans,tree1::dis[i]-tree2::dis[i]); printf("%lld\n",ans); return 0; }