UOJ347 WC2018 通道 边分治、虚树

时间:2023-03-09 13:37:24
UOJ347 WC2018 通道 边分治、虚树

传送门


毒瘤数据结构题qwq

设三棵树分别为$T1,T2,T3$

先将$T1$边分治,具体步骤如下:

①多叉树->二叉树,具体操作是对于每一个父亲,建立与儿子个数相同的虚点,将父亲与这些虚点穿成一条链(父亲在链顶),在虚点的另一边接上儿子,之前父亲到儿子的边权移动到虚点到这个儿子的边上。代码长下面这样

    void rebuild(int x , int f){
        int pre = ++cntNode , p = x;//pre是当前虚点的编号
        for(int i = Thead[x] ; i ; i = TEd[i].upEd)
            if(TEd[i].end != f){//连接上一个虚点、当前虚点和一个儿子。
                addEd(Ed , head , cntEd , x , pre , );
                addEd(Ed , head , cntEd , pre , x , );
                addEd(Ed , head , cntEd , pre , TEd[i].end , TEd[i].w);
                addEd(Ed , head , cntEd , TEd[i].end , pre , TEd[i].w);
                rebuild(TEd[i].end , p);
                x = pre;//替换当前接的虚点
                pre = ++cntNode;
            }
    }

②选择一条边,使得两侧的子树大小差距尽可能小;③计算经过这条边的路径的答案;④分治旁边的两个子树

至于为什么不用点分治……等会儿再提

设我们分治的边为$(s,t)$,切掉这条边之后的两棵子树分别为$L,R$,设$dis1_i$表示$i$到$(s,t)$边的距离,$dis_{Tx}(i,j)$表示在第$x$棵树上$i$到$j$的距离

那么我们在③步骤中需要求的就是满足$i \in L , j \in R$的$$ans=dis_1i+dis_1j+w(s,t)+dis_{T2}(i,j)+dis_{T3}(i,j)$$的最大值

稍微魔改一下式子,设在$T2$上根到$i$的路径上所有边的边权和为$dis_2i$

那么$ans=dis_1i+dis_1j+dis_2i+dis_2j-2 \times dis_2LCA(i,j)+dis_{T3}(i,j)+w(s,t)$

我们考虑枚举$LCA(i,j)$,那么$dis_2LCA(i,j)$与$w(s,t)$对答案最大值就不会产生影响了,会产生影响的部分就是

$$delta = dis_1i+dis_1j+dis_2i+dis_2j+dis_{T3}(i,j)$$

我们把$dis_1i+dis_2i$看做在$T3$上新增了一个$i'$点,与$i$分属同一子树($L$或$R$),且只与$i$连了一条权值为$dis_1i+dis_2i$的边。我们设新的树为$T3'$,那么我们需要在$T3'$上找到两个点$x,y$,满足$x \in L , y \in R$且$dis_{T3'}(x,y)$最大

到这里我们需要一个结论帮助:如果点集$A$中最长路的两个端点为$u,v$,点集$B$中最长路的两个端点为$x,y$,则跨过$A,B$的最长路的端点可能的组合只有$(u,x)(u,y)(v,x)(v,y)$。这意味着我们可以用树形$DP$维护$T2$中子树内所有点在$T3'$上的最长路对应的两个端点。

考虑在$T2$上对$L \cup R$的点建立虚树。设$f_{i,0/1}$表示在$T2$树上$i$的子树中,且属于$T1$中$L/R$子树,在$T3'$上取到最长路的两个点,合并直接把四个端点拿出来一一在$T3'$上算路径长度取$max$即可。我们在合并答案的时候计算贡献,也就是说在合并$f_{i,0/1}$和$f_{son_i,0/1}$的时候跑一下$T3$上最大值贡献答案。求出距离之后,这时两端点的$LCA$必定是$i$,再减掉$2 \times dis2_i$、加上$w(s,t)$就能够得到$ans$了。

那么为什么不使用点分治也就很明了了。边分治必定将原树分成两个子树,但是点分治可能会分成很多,很多的子树之间的合并会很麻烦,所以在这种方法中点分治不能用…当然似乎每一次合并最小的两个子树在点分治下和边分治就是相同的。

代码