【UOJ#400】暴力写挂

题目链接

题意

两棵树 , 求出下面式子的最大值。

\[dep[u]+dep[v]-dep[LCA(u,v)]-dep'[LCA'(u,v)]\]

Sol

边分治。

与第一棵树有关的信息比较多,所以对第一棵树边分。
\(LCA\) 在分治中不好处理 ,因为我们要换根还要快速合并路径信息,那么把式子变个形:
\[\frac{dep[u]+dep[v]+dis[u][v]}{2}-dep'[LCA'(u,v)]\]

这个样子的话在边分的过程中就可以直接把 一个点的深度与它到当前分治边的某个端点的距离作为点权了。这样我们只需要快速求出在第二棵树中选出两个不在同一集合中的点使得他们的权值减去他们的 LCA 在第二棵树中的深度的最大值。
直接虚树 + 树形dp 就可以了。
其实这个题目还是比较板的,没有太大的思维难度。

#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
    x=0;char ch=getchar();bool t=0;
    for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
    if(t) x=-x;return;
}
struct edge{int to,next,w;};
const int N=4e5+10;
typedef long long ll;
typedef vector<int> ary;
const ll INF=1e16;
int n,Tn;
ll *Val,UPD;
int S[2][N];
ll ans=0;
namespace Tree2{
    edge a[N<<1];
    int head[N],cnt=0,bel[N];
    int st[20][N<<1],id[N],log[N<<1],I=0,dep[N];ll dis[N];
    inline void add(int x,int y,int z){a[++cnt]=(edge){y,head[x],z};head[x]=cnt;}
    void dfs(int u,int fa){
        st[0][++I]=u,id[u]=I;
        for(int v,i=head[u];i;i=a[i].next){
            v=a[i].to;if(v==fa) continue;
            dep[v]=dep[u]+1,dis[v]=dis[u]+a[i].w;
            dfs(v,u);st[0][++I]=u;
        }
        return;
    }
    inline int CK(int u,int v){if(!u||!v)return u|v;return dep[u]<dep[v]? u:v;}
    inline int LCA(int u,int v){
        if(u==v) return u;int l=id[u],r=id[v];if(l>r) swap(l,r);
        int D=log[r-l+1]-1;
        return CK(st[D][l],st[D][r-(1<<D)+1]);
    }
    inline ll Query(int u,int v){return dis[LCA(u,v)];}
    void Build(){
        int u,v,w;
        for(int i=1;i<Tn;++i) init(u),init(v),init(w),add(u,v,w),add(v,u,w);
        dfs(1,0);
        for(int i=1;i<=I;++i) if((1<<log[i-1])<i) log[i]=log[i-1]+1;else log[i]=log[i-1];
        for(int k=1;k<=log[I];++k)
            for(int i=1;i+(1<<k)-1<=I;++i)
                st[k][i]=CK(st[k-1][i],st[k-1][i+(1<<k-1)]);
        cnt=0;Set(head,0);Set(bel,-1);
        return;
    }
    namespace Vitual_Tree{
        int que[N],tot=0;
        int stk[N],top=0;
        inline bool cmp(int u,int v){return id[u]<id[v];}
        ll F[2][N];
        inline void Insert(int u){
            if(!top) return void(stk[++top]=u);
            int lca=LCA(u,stk[top]);
            if(lca==stk[top]) return void(stk[++top]=u);
            while(top>=2&&id[stk[top-1]]>=id[lca]) add(stk[top-1],stk[top],0),--top;
            if(stk[top]!=lca) add(lca,stk[top],0),stk[top]=lca;
            stk[++top]=u;return;
        }
        void Dfs(int u){
            F[0][u]=F[1][u]=-INF;
            if(~bel[u]) F[bel[u]][u]=Val[u];
            for(int v,i=head[u];i;i=a[i].next){
                v=a[i].to;Dfs(v);
                ans=max(ans,((F[0][u]+F[1][v]+UPD)/2)-dis[u]);
                ans=max(ans,((F[1][u]+F[0][v]+UPD)/2)-dis[u]);
                F[0][u]=max(F[0][u],F[0][v]);
                F[1][u]=max(F[1][u],F[1][v]);
            }
            head[u]=0;bel[u]=-1;
        }
        void work(){tot=top=cnt=0;
            for(int i=1;i<=S[0][0];++i) bel[S[0][i]]=0,que[++tot]=S[0][i];
            for(int i=1;i<=S[1][0];++i) bel[S[1][i]]=1,que[++tot]=S[1][i];
            sort(que+1,que+1+tot,cmp);
            if(que[1]!=1) stk[top=1]=1;
            for(int i=1;i<=tot;++i) Insert(que[i]);
            while(top>1) add(stk[top-1],stk[top],0),--top;
            Dfs(1);return;
        }
    }using Vitual_Tree::work;
}
namespace Tree1{
    const int MAXN=N<<2;
    edge a[MAXN<<1];
    int head[MAXN],cnt=0,size[MAXN];ll dep[MAXN],dis[MAXN],Ret[MAXN];
    bool vis[MAXN];
    inline void add(int x,int y,int z){a[cnt]=(edge){y,head[x],z};head[x]=cnt++;}
    inline void add_edge(int u,int v,int z){add(u,v,z),add(v,u,z);return;}
    ary son[MAXN];
    void dfs(int u,int fa){
        for(int v,i=head[u];~i;i=a[i].next){
            v=a[i].to;if(v==fa) continue;
            son[u].push_back(v);dep[v]=dep[u]+a[i].w;Ret[v]=a[i].w;
            dfs(v,u);
        }
        return;
    }
    int Cedge;
    inline void Rebuild(){
        Tn=n;cnt=0;Set(head,-1);
        for(int i=1;i<=n;++i) {
            int snum=son[i].size();
            if(snum<=2) {
                for(int j=0;j<snum;++j) add_edge(i,son[i][j],Ret[son[i][j]]);
            }else{
                int sl=++n;int sr=++n;
                add_edge(i,sl,0),add_edge(i,sr,0);
                for(int j=0;j<snum;++j) {
                    if(j&1) son[sl].push_back(son[i][j]);
                    else    son[sr].push_back(son[i][j]);
                }
            }
        }
        return;
    }
    inline void Build(){
        Set(head,-1);int u,v,w;
        for(int i=1;i<n;++i) {init(u),init(v),init(w);add_edge(u,v,w);}
        dfs(1,0);Rebuild();
        return;
    }
    int Mx,Tot;
    void Find(int u,int fa){
        size[u]=1;
        for(int v,i=head[u];~i;i=a[i].next){
            v=a[i].to;if(v==fa||vis[i>>1]) continue;
            Find(v,u);size[u]+=size[v];
            int dat=max(size[v],Tot-size[v]);
            if(dat<Mx) Mx=dat,Cedge=i;
        }return;
    }
    void Dfs(int u,int fa,int*S,ll Dep){
        dis[u]=dep[u]+Dep;
        if(u<=Tn) S[++S[0]]=u;
        for(int v,i=head[u];~i;i=a[i].next){
            v=a[i].to;if(v==fa||vis[i>>1]) continue;
            Dfs(v,u,S,Dep+a[i].w);
        }
        return;
    }
    inline void Divide(int u,int siz){
        Mx=1e9;Tot=siz;Find(u,0);
        if(Mx>=1e9) return;vis[Cedge>>1]=1;
        int rtl=a[Cedge].to,rtr=a[Cedge^1].to;UPD=a[Cedge].w;
        S[0][0]=S[1][0]=0;Dfs(rtl,0,S[0],0),Dfs(rtr,0,S[1],0);
        Tree2::work();int szl=size[rtl];
        Divide(rtl,szl);Divide(rtr,siz-szl);
        return;
    }
    inline void Solve(){Val=dis;Divide(1,n);return;}
}
int main()
{
    init(n);
    Tree1::Build();
    Tree2::Build();
    Tree1::Solve();
    for(int i=1;i<=Tn;++i) ans=max(ans,Tree1::dep[i]-Tree2::dis[i]);
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/NeosKnight/p/10520173.html