BZOJ 5341: [Ctsc2018]暴力写挂 边分治+虚树

和【洛谷5115】挺像的.  

都是统计信息的时候是包含两个树的,那就在一个树上边分治,另一个树上跑一个虚树dp就好了.  

式子这么拆:  

$dep[i]+dep[j]-(dep[LCA(i,j)]+dep'[LCA'(i,j)]$

$\Rightarrow dep[i]+dep[j]-\frac{1}{2}(dep[i]+dep[j]-dis(i,j)]-dep'[LCA'(i,j)])$

$\Rightarrow \frac{1}{2}(dep[i]+dep[j]+dis(i,j)-2dep'[LCA'(i,j)])$

其中 $dis(i,j)$ 可以拆成 $dis(i,u)+dis(j,v)+val(u,v)$,然后这个就用边分治来跑,把关键点扔到虚树上就行了 .

 code:

#include <cstdio>  
#include <string>
#include <algorithm> 
#include <cstring> 
#include <vector>  

#define N 1000002   
#define ll long long 
#define inf 0x3f3f3f3f 

using namespace std;  

int n;     
ll ans=-inf,W;     

namespace IO {  

    void setIO(string s) 
    {
        string in=s+".in"; 
        string out=s+".out"; 
        freopen(in.c_str(),"r",stdin);  
    }  

};  

namespace tree2 {     

    vector<int>G[N],clr;  
    ll dis[N]; 
    int edges,t,sta,tot;    
    int hd[N],to[N<<1],nex[N<<1],dep[N];    
    ll val[N<<1]; 
    int size[N],son[N],top[N],fa[N],dfn[N],re[N];  
    int S[N],col[N]; 
    ll value[N],max1[N],max2[N];           

    void add(int u,ll v,int c) 
    { 
        nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c;  
    }  

    bool cmp(int a,int b)
    { 
        return dfn[a]<dfn[b];   
    } 

    void add_vir(int u,int v) 
    { 
        G[u].push_back(v);  
    } 

    void dfs1(int u,int ff) 
    { 
        fa[u]=ff,size[u]=1,dfn[u]=++t;  
        for(int i=hd[u];i;i=nex[i])
        { 
            int v=to[i]; 
            if(v==ff) continue;   
            dep[v]=dep[u]+1; 
            dis[v]=dis[u]+val[i];   
            dfs1(v,u); 
            size[u]+=size[v];   
            if(size[v]>size[son[u]]) son[u]=v; 
        }     
    } 

    void dfs2(int u,int tp) 
    {
        top[u]=tp; 
        if(son[u]) dfs2(son[u],tp); 
        for(int i=hd[u];i;i=nex[i]) 
            if(to[i]!=fa[u]&&to[i]!=son[u]) dfs2(to[i],to[i]); 
    }

    int LCA(int x,int y) 
    {
        while(top[x]!=top[y]) 
        {
            dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];  
        }
        return dep[x]<dep[y]?x:y;  
    } 

    void newnode(int x,ll v,int c) 
    {  
        re[++tot]=x;  
        col[x]=c;    
        value[x]=v;   
    } 

    void Insert(int x) 
    {   
        if(sta<=1) 
        {
            S[++sta]=x;   
            return;   
        }            
        int lca=LCA(x,S[sta]);     
        if(S[sta]==lca)  S[++sta]=x;   
        else 
        {
            while(sta>1&&dep[S[sta-1]]>=dep[lca]) add_vir(S[sta-1],S[sta]),--sta;   
            if(S[sta]==lca)  S[++sta]=x;  
            else 
            {
                add_vir(lca,S[sta]);  
                S[sta]=lca;   
                S[++sta]=x;  
            }
        }
    }  

    void DP(int x) 
    {             
        clr.push_back(x);  
        max1[x]=max2[x]=-100000000000000000;    
        for(int i=0;i<G[x].size();++i) 
        {
            int y=G[x][i];  
            DP(y);     
            ans=max(ans,max1[x]+max2[y]+W-dis[x]*2);   
            ans=max(ans,max2[x]+max1[y]+W-dis[x]*2);         
            max1[x]=max(max1[x],max1[y]);   
            max2[x]=max(max2[x],max2[y]);   
        }    
        if(col[x]==1)  ans=max(ans,value[x]+max2[x]+W-dis[x]*2),max1[x]=max(max1[x],value[x]); 
        if(col[x]==2)  ans=max(ans,value[x]+max1[x]+W-dis[x]*2),max2[x]=max(max2[x],value[x]);    
    }  

    void solve() 
    { 
        sort(re+1,re+1+tot,cmp);   
        if(re[1]!=1) S[++sta]=1;         
        for(int i=1;i<=tot;++i)  Insert(re[i]);   
        while(sta>1)  add_vir(S[sta-1],S[sta]),--sta;    
        DP(1);       
        for(int i=0;i<clr.size();++i) 
        {
            int x=clr[i];   
            max1[x]=max2[x]=value[x]=col[x]=0;           
            G[x].clear();   
        }                      
        clr.clear();      
        tot=sta=0;              
    }  

    void Initialize() 
    {     
        dfs1(1,0); 
        dfs2(1,1);   
    }    

};  

namespace tree1 {  

    int tot,totsize,rt1,rt2,ed,mx;  
    int edges1=1,edges2=1;   
    int hd[N],pre[N<<1],vis[N<<2],size[N<<1]; 
    ll dis[N<<1];  

    struct Edge { 
        int to; 
        ll w; 
        int nex;  
    }e[N<<2],edge[N<<2];    

    void add(int u,ll v,int c) 
    {
        e[++edges1].nex=pre[u],pre[u]=edges1,e[edges1].to=v,e[edges1].w=c;   
    }

    void add_div(int u,ll v,int c) 
    {    
        edge[++edges2].nex=hd[u],hd[u]=edges2,edge[edges2].to=v,edge[edges2].w=c;  
    } 

    void getdis(int u,int ff) 
    {   
        for(int i=pre[u];i;i=e[i].nex) 
        { 
            int v=e[i].to; 
            if(v==ff) continue;   
            dis[v]=dis[u]+e[i].w;   
            getdis(v,u);  
        }
    }  

    void Build_Tree(int u,int fa) 
    {  
        int ff=0; 
        for(int i=pre[u];i;i=e[i].nex) 
        {   
            int v=e[i].to;  
            if(v==fa) continue;    
            if(!ff) 
            {   
                ff=u;   
                add_div(u,v,e[i].w);  
                add_div(v,u,e[i].w);  
            }
            else
            {
                ++tot;     
                add_div(ff,tot,0);  
                add_div(tot,ff,0); 
                add_div(tot,v,e[i].w);   
                add_div(v,tot,e[i].w);         
                ff=tot;  
            }
            Build_Tree(v,u);  
        }
    }      

    void find_edge(int x,int fa) 
    {  
        size[x]=1;    
        for(int i=hd[x];i;i=edge[i].nex) 
        {
            int y=edge[i].to;   
            if(y==fa||vis[i])  continue;   
            find_edge(y,x);   
            int now=max(size[y],totsize-size[y]);           
            if(now<mx) 
            {
                mx=now;    
                rt1=y;   
                rt2=x;                
                ed=i;  
            }
            size[x]+=size[y]; 
        }
    }  

    void dfs(int u,int ff,ll d,int ty) 
    {        
        if(u<=n) 
        {
            tree2::newnode(u,dis[u]+d,ty);     
        }
        for(int i=hd[u];i;i=edge[i].nex) 
        {
            int v=edge[i].to;   
            if(v==ff||vis[i]) continue;    
            dfs(v,u,d+edge[i].w,ty); 
        }
    } 

    void Divide_And_conquer(int x) 
    {     
        if(totsize==1) return;  
        rt1=rt2=ed=0,mx=inf;   
        find_edge(x,0);          
        vis[ed]=vis[ed^1]=1;            
        W=edge[ed].w;       
        dfs(rt1,0,0,1);  
        dfs(rt2,0,0,2);  
        tree2::solve();          
        int sizert1=size[rt1],sizert2=totsize-size[rt1];  
        int tmprt1=rt1,tmprt2=rt2;    
        totsize=sizert1;    
        Divide_And_conquer(tmprt1);   
        totsize=sizert2;  
        Divide_And_conquer(tmprt2);   
    }
}; 

int main() 
{ 
    // IO::setIO("input"); 
    int i,j;    
    scanf("%d",&n);      
    for(i=1;i<n;++i) 
    {
        int x,y,z; 
        scanf("%d%d%d",&x,&y,&z);  
        tree1::add(x,y,(ll)z);   
        tree1::add(y,x,(ll)z); 
    }
    for(i=1;i<n;++i) 
    {
        int x,y,z; 
        scanf("%d%d%d",&x,&y,&z);          
        tree2::add(x,y,(ll)z); 
        tree2::add(y,x,(ll)z);  
    } 
    tree2::Initialize();   
    tree1::tot=n;    
    tree1::getdis(1,0);   
    tree1::Build_Tree(1,0);               
    tree1::totsize=tree1::tot;   
    tree1::Divide_And_conquer(1);    
    ans>>=1;    
    for(i=1;i<=n;++i)  ans=max(ans,tree1::dis[i]-tree2::dis[i]);    
    printf("%lld\n",ans); 
    return 0;   
}

  

posted @ 2019-12-28 14:25  EM-LGH  阅读(144)  评论(0编辑  收藏  举报