[树分治 虚树] LOJ#2339. 「WC2018」通道

考场打了树分治套链分治拿了64分…

大概就是把第三棵树树分治,(用左儿子右兄弟的方法重构),然后在第二棵树建立虚树,然后树形DP,每个点记录子树的点在第一棵树中距离最大的两个点,然后合并

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <assert.h>
#define show(x) cout<<'\t'<<#x<<' '<<x<<endl

using namespace std;

typedef long long ll;
typedef pair<int,int> PII;

const int N=100010;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

inline void read(ll &x){
  char c=nc(); x=0;
  for(;c>'9'||c<'0';c=nc());for(;c>='0'&&c<='9';x=x*10+c-'0',c=nc());
}

struct edge{
  int t,nx; ll w;
};

int n,lg2[N<<1];

struct tree{
  int G[N],lst[N<<1],p[N],dpt[N],t,cnt;
  ll cst[N];
  edge E[N<<1];
  int st[N<<1][20];

  void dfs(int x,int f){
    lst[++t]=x; p[x]=t; dpt[x]=dpt[f]+1;
    for(int i=G[x];i;i=E[i].nx)
      if(E[i].t!=f){
    cst[E[i].t]=cst[x]+E[i].w;
    dfs(E[i].t,x);
    lst[++t]=x;
      }
  }

  void Pre(){
    int n=lg2[t];
    for(int i=1;i<=t;i++) st[i][0]=lst[i];
    for(int k=1;k<=n;k++)
      for(int i=1;i+(1<<k)-1<=t;i++)
    st[i][k]=dpt[st[i][k-1]]<dpt[st[i+(1<<k-1)][k-1]]?st[i][k-1]:st[i+(1<<k-1)][k-1];
  }

  inline void addedge(int x,int y,ll w){
    E[++cnt].t=y; E[cnt].nx=G[x]; E[cnt].w=w; G[x]=cnt;
    E[++cnt].t=x; E[cnt].nx=G[y]; E[cnt].w=w; G[y]=cnt;
  }

  inline int Query(int x,int y){
    x=p[x]; y=p[y];
    if(x>y) swap(x,y);
    int t=lg2[y-x+1];
    return dpt[st[x][t]]<dpt[st[y-(1<<t)+1][t]]?st[x][t]:st[y-(1<<t)+1][t];
  }

  inline ll dis(int x,int y){
    if(!x || !y) return -1;
    return cst[x]+cst[y]-2*cst[Query(x,y)];
  }
}T[3];

int G[N],cnt;
struct iedge{
  int t,nx; int d;
}E[N<<1];

inline void addedge(int x,int y,int d){
  //show(x); show(y);
  E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt; E[cnt].d=d;
  E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt; E[cnt].d=1;
}

ll df[N];

void dfs(int *G,edge *E,int x,int f){
  int ad=0,lst=0;
  for(int i=G[x];i;i=E[i].nx){
    if(E[i].t==f) continue;
    df[E[i].t]=E[i].w;
    if(!ad) addedge(x,E[i].t,0),ad=1;
    if(lst) addedge(lst,E[i].t,1); lst=E[i].t;
    dfs(G,E,E[i].t,x);
  }
}

int vis[N],size[N],isz,imax,root;
int *p=T[1].p;

void expl(int x,int f){
  size[x]=1;
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f && !vis[E[i].t]){
      expl(E[i].t,x);
      size[x]+=size[E[i].t];
    }
}

void getroot(int x,int f){
  int imin=0,c=0;
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f && !vis[E[i].t]){
      getroot(E[i].t,x);
      imin=max(imin,size[E[i].t]);
      c+=size[E[i].t];
    }
  imin=max(imin,isz-c);
  if(imin<imax) imax=imin,root=x;
}

ll ans;

namespace IT{
  int G[N],tp[N],cnt,S[N],vis[N],clk,t,root;
  ll cst[N];
  edge E[N<<1];

  tree *T=::T+1;

  inline bool cmp(const int &x,const int &y){
    return p[x]<p[y];
  }

  inline void addedge(int x,int y){
    E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
    E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
  }

  void build(int *Q,int n){
    T=::T+1;
    cnt=0; int nt=n; ++clk;
    for(int i=1;i<=n;i++)
      cst[Q[i]]+=T->cst[Q[i]],vis[Q[i]]=clk;
    sort(Q+1,Q+1+n,cmp);
    for(int i=1;i<nt;i++){
      Q[++n]=T->Query(Q[i],Q[i+1]);
      if(vis[Q[n]]!=clk) tp[Q[n]]=0;
    }
    sort(Q+1,Q+1+n,cmp); n=unique(Q+1,Q+1+n)-Q-1; t=0;
    for(int i=1;i<=n;i++){
      G[Q[i]]=0;
      while(t && T->Query(S[t],Q[i])!=S[t]) t--;
      if(!t) root=Q[i];
      else addedge(S[t],Q[i]);
      S[++t]=Q[i];
    }
  }

  int ver[N][4][2],dver[N][4][2];

  inline ll calc(int *a,int *b){
    //show(a[0]); show(a[1]); show(b[0]); show(b[1]); cout<<endl;
    ll ret=0;
    for(int i=0;i<2;i++)
      for(int j=0;j<2;j++)
    if(a[i] && b[j]) ret=max(ret,T->dis(a[i],b[j])+cst[a[i]]+cst[b[j]]);;
    return ret;
  }

  inline void merge(int *a,int *b){
    ll cur=T->dis(a[0],a[1])+cst[a[0]]+cst[a[1]];
    int x=a[0],y=a[1];
    for(int i=0;i<2;i++)
      for(int j=0;j<2;j++){
    if(T->dis(a[i],b[j])+cst[a[i]]+cst[b[j]]>cur)
      cur=T->dis(a[i],b[j])+cst[a[i]]+cst[b[j]],x=a[i],y=b[j];
      }
    if(T->dis(b[0],b[1])+cst[b[0]]+cst[b[1]]>cur) x=b[0],y=b[1];
    a[0]=x; a[1]=y;
  }

  ll *dcst=::T[1].cst;

  void dfs(int x,int f){
    for(int i=1;i<=3;i++)
      ver[x][i][0]=ver[x][i][1]=dver[x][i][0]=dver[x][i][1]=0;
    if(tp[x]){
      for(int i=1;i<=3;i++){
    if(i==tp[x])
      ver[x][i][0]=ver[x][i][1]=x;
    else
      dver[x][i][0]=dver[x][i][1]=x;
      }
    }
    for(int k=G[x];k;k=E[k].nx){
      int v=E[k].t;
      if(v==f) continue;
      dfs(v,x);
      for(int i=1;i<=3;i++){
    ll cur=max(calc(ver[v][i],dver[x][i]),calc(ver[x][i],dver[v][i]))-2*dcst[x];
    ans=max(ans,cur);
      }
      for(int i=1;i<=3;i++)
    merge(ver[x][i],ver[v][i]),merge(dver[x][i],dver[v][i]);
    }
  }

  void work(){
    T=::T;
    dfs(root,0);
  }
}

int Q[N<<1],tot;

void afs(int x,int f,int c,int d){
  ans=max(ans,T[1].dis(x,root)+T[2].dis(x,root)+T[0].dis(x,root));
  Q[++tot]=x; IT::tp[x]=c; IT::cst[x]=T[2].dis(x,root)-(d?df[root]:-df[root]); 
  for(int i=G[x];i;i=E[i].nx)
    if(E[i].t!=f && !vis[E[i].t]) afs(E[i].t,x,c,d);
}

void solve(int x){
  expl(x,0); imax=1<<30; isz=size[x]; getroot(x,0);
  int cur=root; vis[cur]=1; tot=0;
  for(int i=G[cur],j=1;i;i=E[i].nx,j++)
    if(!vis[E[i].t]) afs(E[i].t,0,j,E[i].d);
  //Q[++tot]=x; IT::tp[x]=0;
  IT::build(Q,tot);
  IT::work();
  for(int i=G[cur];i;i=E[i].nx)
    if(!vis[E[i].t]) solve(E[i].t);
}

void PutAns(ll x){
  if(x>=10) PutAns(x/10); putchar(x%10+'0');
}

int main(){
  read(n);
  for(int i=1;i<=(n<<1);i++) lg2[i]=lg2[i-1]+((1<<lg2[i-1]+1)==i);
  for(int k=0;k<3;k++){
    tree *cur=T+k;
    for(int i=1;i<n;i++){
      int x,y; ll w;
      read(x); read(y); read(w);
      assert(x<=n && y<=n);
      cur->addedge(x,y,w);
    }
    cur->dfs(1,0); cur->Pre();
  }
  dfs(T[2].G,T[2].E,1,0);
  solve(1);
  PutAns(ans); putchar('\n');
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值