天天看點

[LOJ]#2553. 「CTSC2018」暴力寫挂 邊分治+線段樹合并

Solution

這題搞了好久……不過還是挺有收獲的。

聽說這種多棵樹的題大概都是這樣的套路?枚舉第二棵樹的 L C A ( x , y ) LCA(x,y) LCA(x,y),然後化一下式子可以發現: d e p t h x + d e p t h y − d e p t h L C A ( x , y ) = 1 2 ( d e p t h x + d e p t h y + d i s t a n c e x , y ) depth_x+depth_y-depth_{LCA(x,y)}={1\over 2}(depth_x+depth_y+distance_{x,y}) depthx​+depthy​−depthLCA(x,y)​=21​(depthx​+depthy​+distancex,y​)

這樣問題就轉化為在目前枚舉點的兩棵不同子樹中各找一個點,求在第一棵樹上深度和+距離的最大值。這樣枚舉的時候每次把一棵子樹合并過來,一般用可以合并的資料結構,可以 d s u dsu dsu,但這樣是 n l o g 2 n nlog^2n nlog2n的,無法通過。這時候又想到另外一個東西——線段樹合并。但如何用線段樹維護這些資訊呢?有一個與線段樹結構十分相似的東西,邊分樹,同樣是二叉樹的結構,不僅可以維護這些資訊,而且可以友善地合并。

邊分樹的每個葉子節點代表原樹的結點,而其他節點代表的是一條邊,那麼對于每個非葉子節點維護左右兒子的 d e p t h x + depth_x+ depthx​+到該邊距離的最大值即可。

由于邊分治的複雜度跟點的度數有關,是以要把原樹轉成二叉樹,這樣點數仍然是 O ( n ) O(n) O(n)級别。

我的代碼全是自己YY的,是以又長又醜……

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=2200000;
const LL inf=(1LL<<60);
int read()
{
 int x=0,f=1;char ch=getchar();
 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
 return x*f;
}
int n,ls[Maxn],rs[Maxn],fa[Maxn],fir[Maxn],g[Maxn][20],gg=0,dep[Maxn],Log[Maxn];LL dis[Maxn];
int root[Maxn],lc[Maxn*20],rc[Maxn*20],cnt=0;LL lx[Maxn*20],rx[Maxn*20],ans=-inf;
struct Edge{int x,y,d,next;};
struct Graph
{
 Edge e[Maxn<<1];
 int last[Maxn],len;
 void ins(int x,int y,int d)
 {
  int t=++len;
  e[t].x=x;e[t].y=y;e[t].d=d;
  e[t].next=last[x];last[x]=t;
 }
 void addedge(int x,int y,int d){ins(x,y,d),ins(y,x,d);}
}G1,G2,G3;
int tmp[Maxn],lt,id,V[Maxn];
int build(int l,int r)
{
 if(l==r)return tmp[l];
 int x=++id,mid=l+r>>1,lc=build(l,mid),rc=build(mid+1,r);
 G2.addedge(x,lc,(lc<=n)?V[lc]:0),G2.addedge(x,rc,(rc<=n)?V[rc]:0);
 return x;
} 
void dfs1(int x,int ff)
{
 lt=0;
 for(int i=G1.last[x];i;i=G1.e[i].next)
 {
  int y=G1.e[i].y;
  if(y==ff)continue;
  tmp[++lt]=y;V[y]=G1.e[i].d;
 }
 if(!lt)return;
 int rt=build(1,lt);
 if(rt>n)G2.addedge(x,rt,0);else G2.addedge(x,rt,V[rt]);
 for(int i=G1.last[x];i;i=G1.e[i].next)
 {
  int y=G1.e[i].y;
  if(y==ff)continue;
  dfs1(y,x);
 }
} 
int LCA(int x,int y)
{
 if(fir[x]>fir[y])swap(x,y);
 int t=Log[fir[y]-fir[x]+1];
 int p1=g[fir[x]][t],p2=g[fir[y]-(1<<t)+1][t];
 if(dep[p1]<dep[p2])return p1;
 return p2;
}
LL Dis(int x,int y){return dis[x]+dis[y]-dis[LCA(x,y)]*2LL;}
int pp,pd,sz[Maxn],tot,mn,rt;bool ban[Maxn];
int get_size(int x,int ff)
{
 int re=1;
 for(int i=G2.last[x];i;i=G2.e[i].next)
 {
  int y=G2.e[i].y;
  if(y==ff||ban[i])continue;
  re+=get_size(y,x);
 }
 return re;
}
void get_edge(int x,int ff)
{
 sz[x]=1;
 for(int i=G2.last[x];i;i=G2.e[i].next)
 {
  int y=G2.e[i].y;
  if(y==ff||ban[i])continue;
  get_edge(y,x);
  int t=max(sz[y],tot-sz[y]);
  if(t<mn)mn=t,pp=i;
  sz[x]+=sz[y];
 }
}
void dfs2(int x,int y,int z)
{
 mn=2147483647;tot=get_size(x,0);get_edge(x,0);
 if(sz[x]==1)ls[z]=x,fa[x]=z;
 else
 {
  ls[z]=id+pp;fa[id+pp]=z;ban[pp]=ban[pp^1]=true;
  dfs2(G2.e[pp].x,G2.e[pp].y,id+pp);
 }
 mn=2147483647;tot=get_size(y,0);get_edge(y,0);
 if(sz[y]==1)rs[z]=y,fa[y]=z;
 else
 {
  rs[z]=id+pp;fa[id+pp]=z;ban[pp]=ban[pp^1]=true;
  dfs2(G2.e[pp].x,G2.e[pp].y,id+pp);
 }
}
void dfs4(int x,int ff)
{
 fir[x]=++gg;g[gg][0]=x;dep[x]=dep[ff]+1;
 for(int i=G2.last[x];i;i=G2.e[i].next)
 {
  int y=G2.e[i].y;
  if(y==ff)continue;
  dis[y]=dis[x]+G2.e[i].d;
  dfs4(y,x);g[++gg][0]=x;
 }
}
void merge(int &u1,int u2,int ID,LL dd)
{
 if(!u1){u1=u2;return;}
 if(!u2)return;
 ans=max(ans,G2.e[ID-id].d+lx[u1]+rx[u2]-2LL*dd);
 ans=max(ans,G2.e[ID-id].d+lx[u2]+rx[u1]-2LL*dd);
 lx[u1]=max(lx[u1],lx[u2]);
 rx[u1]=max(rx[u1],rx[u2]);
 merge(lc[u1],lc[u2],ls[ID],dd),merge(rc[u1],rc[u2],rs[ID],dd);
}
void dfs3(int x,int ff,LL dd)
{
 ans=max(ans,2LL*dis[x]-2LL*dd);
 for(int i=G3.last[x];i;i=G3.e[i].next)
 {
  int y=G3.e[i].y;
  if(y==ff)continue;
  dfs3(y,x,dd+G3.e[i].d);
  merge(root[x],root[y],rt,dd);
 }
}
int main()
{
 G1.len=0,G2.len=1,G3.len=0;
 n=read();id=n;
 for(int i=1;i<n;i++)
 {
  int x=read(),y=read(),d=read();
  G1.addedge(x,y,d);
 }
 dfs1(1,0);
 dep[0]=-1;dfs4(1,0);
 Log[1]=0;for(int i=2;i<=gg;i++)Log[i]=Log[i>>1]+1; 
 for(int j=1;(1<<j)<=gg;j++)
 for(int i=1;i+(1<<j)-1<=gg;i++)
 {
  int p1=g[i][j-1],p2=g[i+(1<<(j-1))][j-1];
  if(dep[p1]<dep[p2])g[i][j]=p1;
  else g[i][j]=p2;
 }
 tot=id;mn=2147483647;get_edge(1,0);
 rt=id+pp;ban[pp]=ban[pp^1]=true; 
 dfs2(G2.e[pp].x,G2.e[pp].y,rt);
 for(int i=1;i<=n;i++)
 {
  int l1=-1,l2,x=i;
  while(1)
  {
   int t=++cnt; 
   if(l1!=-1)
   {
    if(ls[x]==l1)lc[t]=l2,lx[t]=Dis(G2.e[x-id].x,i)+dis[i],rx[t]=-inf;
    else rc[t]=l2,rx[t]=Dis(G2.e[x-id].y,i)+dis[i],lx[t]=-inf;
   }
   else lx[t]=rx[t]=-inf;
   l1=x,l2=t;
   if(x==rt){root[i]=t;break;}
   x=fa[x];
  }
 }
 for(int i=1;i<n;i++)
 {
  int x=read(),y=read(),d=read();
  G3.ins(x,y,d),G3.ins(y,x,d);
 }
 dfs3(1,0,0);
 printf("%lld",ans/2LL); 
}