uoj#347. 【WC2018】通道 //点分治×虚树

uoj#347. 【WC2018】通道


题意

给出三棵 n ( 10 5 ) 个点的树,边有边权 L i ( 10 12 ) ,求 m a x 1 i , j n ( d i s 1 ( i , j ) + d i s 2 ( i , j ) + d i s 3 ( i , j ) )


写在前面的自说自话

上一次更这边的blog…是两个月之前?最近一直没有写题没遇到比较值得放进blog的题…然后我现在已经不知道什么难度的题应该写blog了…。
本来wc回来就想改的,但是因为考场上留下了「点分×点分写挂」的心理阴影就一直没改(可能还有个原因是当时没看懂题解里最后那句「多叉转二叉」是怎么转的)。至于现在…ctsc「点分×点分写挂」的心理阴影把那次的覆盖了…就可以安心改了(大雾
还是前几天ryf问到这个题才想起来的…正好最近没怎么写长代码题(如果考场上不算的话),翻出来写一写,然后写了半下午,调了半下午。


题解

大家可以去看课件呀
稍微简述一下…
两棵树的情况可以点分套点分考虑在 T 1 的每个点 i 上挂上 d e p 2 ( i ) 的贡献,之后在 T 2 上枚 l c a ,把不在同一子树里的点对拎出来,到 T 1 里求 m a x
摘一条性质:「边权非负的图中,跨越集合 A , B 的最长链的端点,一定是 A 中最长链和 B 中最长链的端点」
所以就可以在 T 2 里自底向上合并,维护子树在 T 1 中对应的最长链端点,因为带个 T 1 里求 l c a ,复杂度带 l o g
树*2+链就对 T 3 那条链分治,计算过中点路径的贡献。把中点两边的点作为两种类型,贡献算出来,拿到一起建个 T 2 上的虚树,合并时分别维护两种类型的点集即可。
(这步我看了半天一直觉得是双log的…因为虚树dfs序带个排序,但题解里似乎并不是…于是问l1ll5虚树有没有线性做法,l1ll5看了一眼题,告诉我这个在分治过程中可以归并上去…感觉自己很sb。)
树*3同理对 T 3 点分。因为对于分治重心的每个分治子树,需要分别算它到其它子树的最长链,重心度数大就跪了,所以需要预处理转个二叉,也即在当前根的位置建一条链,边权都为零,之后把它的子节点依次挂到链上。
总复杂度 O ( n l o g n )
为什么题解里带了个 α


代码

#include<bits/stdc++.h>
#define N 200005
#define L 18
#define rg register int
#define upd(x) ans<x?ans=x:0
using namespace std;
typedef long long ll;
int n,nn,d=-1,to[N],hd[N],lk[N],cnt,t,sz[N],a[N],b[N],tot;
ll len[N],ans,dep[N][L];
struct typ1
{
    ll D[N];
    int ln[N][L],st[N],de[N],re[N];
    inline int lca(int x,int y)
    {
        if(x==y)return x;
        x=re[x],y=re[y];if(x>y)swap(x,y);
        rg z=st[y-x],u=ln[x][z],v=ln[y-(1<<z)+1][z];
        return de[u]<de[v]?u:v;
    }
    inline ll dis(int x,int y)
    {return D[x]+D[y]-(D[lca(x,y)]<<1);}
    void dfs(int x)
    {
        ln[re[x]=t++][0]=x;
        for(rg s,i=lk[x];i;i=hd[i])
        if(!de[s=to[i]])
        de[s]=de[x]+1,D[s]=D[x]+len[i],dfs(s),ln[t++][0]=x;
    }
    inline void rmq()
    {
        de[1]=1,dfs(1);
        for(rg i=1;i<t;i++)
        st[i]=st[i-1]+!!(i>>st[i-1]);
        for(rg i=t-1;i>=0;--st[i--])
        for(rg j=0;j<L-1&&i+(1<<j)<t;j++)
        ln[i][j+1]=ln[i+((de[ln[i][j]]>de[ln[i+(1<<j)][j]])<<j)][j];
        t=0;
    }
}T1,T2;
ll w,D[N],now,tmp;
inline void add(int u,int v)
{to[++cnt]=v,hd[cnt]=lk[u],len[cnt]=w,lk[u]=cnt;}
inline void cal(int u,int v)
{w=T2.D[v]-T2.D[u],add(u,v);}
int st[N],rec[N],no[N][4],u,v,tim[N],dfn;
bool ins[N];
inline ll dis(int x,int y)
{return x&&y?D[x]+D[y]+T1.dis(x,y):-1;}
inline void mx(int x,int y)
{tmp=dis(x,y);if(tmp>now)u=x,v=y,now=tmp;}
inline void find(int p,int q,int r,int s)
{now=-2,mx(p,r),mx(q,r),mx(p,s),mx(q,s);}
inline void find_(int p,int q,int r,int s)
{find(p,q,r,s);mx(p,q),mx(r,s);}
inline void uni(int x,int y)
{
    find(no[x][0],no[x][2],no[y][1],no[y][3]);
    upd(dis(u,v)-(w<<1));
    find(no[x][1],no[x][3],no[y][0],no[y][2]);
    upd(dis(u,v)-(w<<1));
    find_(no[x][0],no[x][2],no[y][0],no[y][2]);
    no[x][0]=u,no[x][2]=v;
    find_(no[x][1],no[x][3],no[y][1],no[y][3]);
    no[x][1]=u,no[x][3]=v;
}
void sol(int x)
{
    register ll tp=D[x];D[x]+=dep[x][d];
    no[x][ins[x]]=no[x][ins[x]|2]=tim[x]==dfn?x:0;
    no[x][ins[x]^1]=no[x][2|ins[x]^1]=0;
    for(rg s,i=lk[x];i;i=hd[i])
    D[s=to[i]]=tp+len[i],sol(s),w=tp,uni(x,s);
}
inline void merge(int l,int m,int r)
{
    tot=l;
    for(rg i=l,j=m;i<m||j<r;)
    b[tot++]=a[(j==r||i<m&&T2.re[a[i]]<T2.re[a[j]])?i++:j++];
    dfn++;tot=cnt=0;
    for(rg i=l,x,y,lst;i<r;i++)
    {
        ins[a[i]]=i<m,x=a[i]=b[i],tim[x]=dfn,lk[x]=0;
        if(tot)
        {
            y=T2.lca(st[tot],x);
            for(lst=0;T2.de[st[tot]]>T2.de[y];tot--)
            {
                if(lst)cal(st[tot],lst);
                lst=st[tot];
            }
            if(st[tot]^y)
            lk[st[++tot]=y]=0;
            if(lst)cal(y,lst);
        }
        st[++tot]=x;
    }
    for(;tot>1;tot--)cal(st[tot-1],st[tot]);
    sol(st[1]);
}
struct typ2
{
    int To[N<<1],Hd[N<<1],Lk[N],tp;
    ll Len[N<<1];
    bool vis[N];
    inline void Add(int u,int v,ll w)
    {To[++cnt]=v,Hd[cnt]=Lk[u],Len[cnt]=w,Lk[u]=cnt;}
    inline void ad(int u,int v,ll w)
    {Add(u,v,w),Add(v,u,w);}
    void dfs(int x)
    {
        vis[x]=sz[x]=1;
        if(hd[hd[lk[x]]]&&x<2||hd[hd[hd[lk[x]]]])
        {
            rg lst=0;
            for(rg s,i=lk[x];i;i=hd[i])
            if(!vis[s=to[i]])
            {
                dfs(s),ad(++n,s,len[i]),sz[n]=sz[s]+1;
                if(lst)ad(n,lst,0),sz[n]+=sz[lst];
                lst=n;
            }
            ad(x,n,0),sz[x]+=sz[n];
        }
        else for(rg s,i=lk[x];i;i=hd[i])
        if(!vis[s=to[i]])
        ad(x,s,len[i]),dfs(s),sz[x]+=sz[s];
    }
    int getr(int x,int y)
    {
        for(rg s,i=Lk[x];i;i=Hd[i])
        if(sz[s=To[i]]>tp&&vis[s]&&s^y)
        return getr(s,x);
        return x;
    }
    void ini(int x,int y)
    {
        sz[x]=1;
        for(rg s,i=Lk[x];i;i=Hd[i])
        if(vis[s=To[i]]&&s^y)
        dep[s][d]=dep[x][d]+Len[i],
        ini(s,x),sz[x]+=sz[s];
    }
    void cet(int x,int y)
    {
        if(x<=nn)
        upd(dep[x][d]+T1.dis(x,tp)+T2.dis(x,tp));
        for(rg s,i=Lk[x];i;i=Hd[i])
        if(rec[s=To[i]]>rec[tp]&&s^y)
        cet(s,x);
    }
    void div(int x)
    {
        tp=sz[x]>>1;
        vis[x=getr(x,x)]=0;
        rec[x]=d++;ini(x,x);
        rg l=t;
        for(rg i=Lk[x],r=t,s;i;i=Hd[i])
        if(vis[s=To[i]])
        div(s),l<r&&r<t?merge(l,r,t),0:0,r=t;
        if(x<=nn)
        {
            for(rg i=t-1;;i--)
            if(i<l||T2.re[a[i]]<T2.re[x])
            {
                for(rg j=t;j>i;j--)
                a[j]=a[j-1];a[i+1]=x;
                break;
            }
            t++,tp=x,cet(x,x);
        }
        d--;
    }
}T3;
inline void ini()
{
    for(rg i=1;i<=n;i++)lk[i]=0;
    for(rg i=1,u,v;i<n;++i)
    scanf("%d%d%lld",&u,&v,&w),
    add(u,v),add(v,u);
    cnt=0;
}
int main()
{
    scanf("%d",&n);nn=n;
    ini(),T1.rmq();
    ini(),T2.rmq();
    ini(),T3.dfs(1);
    for(rg i=nn+1;i<=n;i++)T3.vis[i]=1;
    T3.div(1);
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/Starria/article/details/80425789