直径 - 树dp

题目大意:给你一颗树T,1为根。
新建m颗树为森林,每颗树都是T的一个子树。
将这个森林连接起来,每次形如把第a颗树中的b点和第c颗树中的d点连起来。
最后求这玩意的直径。
题解:
显然可以虚树dp,没写。
考虑首先对T的每个点求其子树直径。
有个结论是这样的,两个树连一条边形成一颗新的树,新的树的直径的端点是原两颗树的直径的四个端点中选择某两个。
然后dfs那个询问树,像朴素dp一样更新答案维护最长链什么的就可以了。


#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<utility>
#include<vector>
#define lint long long
#define gc getchar()
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define N 300010
#define LOG 22
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
using namespace std;
typedef pair<int,int> pii;
typedef pair<int,pii> piii;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct edge{
    int to,pre;
}e[N<<1];int h[N],etop,d[N],up[N][LOG],Log[N],r[N];vector<piii> g[N];
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
struct node{
    int x,y;lint dx,dy,dis;
    node(int _x=0,int _y=0,lint _dx=0,lint _dy=0,lint _dis=0)
    {   x=_x,y=_y,dx=_dx,dy=_dy,dis=_dis;   }
    inline bool operator<(const node &n)const{return dis<n.dis;}
}f[N],l[N],t[10];lint ans;
int dfs(int x,int fa=0)
{
    d[x]=d[up[x][0]=fa]+1;rep(i,1,Log[d[x]]) up[x][i]=up[up[x][i-1]][i-1];
    for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa) dfs(y,x);return 0;
}
inline int getLCA(int x,int y)
{
    if(d[x]<d[y]) swap(x,y);
    for(int i=Log[d[x]];i>=0;i--) if(up[x][i][d]>=y[d]) x=up[x][i];if(x==y) return x;
    for(int i=Log[d[x]];i>=0;i--) if(up[x][i]^up[y][i]) x=up[x][i],y=up[y][i];
    return up[x][0];
}
inline int gdis(int x,int y) { return d[x]+d[y]-2*d[getLCA(x,y)]; }
int getl(int x,int fa=0)
{
    l[x]=node(x,x,0,0,0);
    for(int i=h[x],y;i;i=e[i].pre) if((y=e[i].to)^fa)
    {
        getl(y,x),t[1]=node(l[x].x,l[y].x),t[2]=node(l[x].x,l[y].y),
        t[3]=node(l[x].y,l[y].x),t[4]=node(l[x].y,l[y].y),l[x]=max(l[x],l[y]);
        rep(j,1,4) t[j].dis=gdis(t[j].x,t[j].y),l[x]=max(l[x],t[j]);
    }
    return 0;
}
int getans(int x,int fa=0)
{
    f[x]=l[r[x]],f[x].dx=f[x].dy=0;int y,a,b;lint bd;
    Rep(i,g[x]) if((y=g[x][i].fir)^fa)
        a=g[x][i].sec.fir,b=g[x][i].sec.sec,getans(y,x),
        bd=max(gdis(b,f[y].x)+f[y].dx,gdis(b,f[y].y)+f[y].dy),
        t[1]=node(f[x].x,a,f[x].dx,1+bd,gdis(f[x].x,a)+1+bd+f[x].dx),
        t[2]=node(f[x].y,a,f[x].dy,1+bd,gdis(f[x].y,a)+1+bd+f[x].dy),
        f[x]=max(f[x],max(t[1],t[2]));
    return ans=max(ans,f[x].dis),0;
}
int main()
{
    int n=inn(),m=inn(),u,v;rep(i,2,n) Log[i]=Log[i>>1]+1;
    rep(i,1,n-1) u=inn(),v=inn(),add_edge(u,v),add_edge(v,u);
    dfs(1),getl(1);rep(i,1,m) r[i]=inn();int a,b,c,d;
    rep(i,1,m-1) a=inn(),b=inn(),c=inn(),d=inn(),g[a].pb(mp(c,mp(b,d))),g[c].pb(mp(a,mp(d,b)));
    return getans(1),!printf("%lld\n",ans+1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值