边分

说明

树分治的一种,与点分相似,每次找到两边节点数量相对接近的一条边(与重心相似),然后考虑经过这条边的路径,之后在对边的两边分别考虑。
对于菊花图,如果直接边分,那么复杂度显然会由O(logn)退化成O(n),因此在边分之前,先要rebuild:
统计出每个节点的儿子节点个数,若它大于S(一般为2,可以根据实际情况调整),则在其儿子节点改为S个虚点,与它们连虚边,原来的儿子节点平均分配到S个虚点下面,原来的儿子节点与虚点的边就是它们与原来的父节点之间的边,虚点的点权和虚边的边权则根据实际情况调整。
相比于点分,它每次仅将原来的树分为两个,而点分将会分为多个,边分处理起来相对容易,它的缺点在于要rebuild,因而常数相对较大。

代码

#define P pair<int,int>
#define mp make_pair
#define fi first
#define se second
inline void rebuild()
{
    memset(first,-1,sizeof(first)),bb=1;
    int i,j,p,q;
    for(i=1; i<=n; i++)
    {
        if(son[i].size()<=2)
        {
            for(j=0; jelse
        {
            p=++n,q=++n;
            for(j=0; jif(j&1) son[p].push_back(son[i][j]),qn[p].push_back(qn[i][j]);
                else son[q].push_back(son[i][j]),qn[q].push_back(qn[i][j]);
            }
            son[i].clear(),qn[i].clear();
            son[i].push_back(p),son[i].push_back(q);
            qn[i].push_back(0),qn[i].push_back(0);
            ad(i,p,0),ad(i,q,0);
        }
    }
}

void gs(int now,int last)
{
    int p,q;
    size[now]=1;
    for(p=first[now]; p!=-1; p=bn[p].next)
    {
        if(vis[p>>1] || bn[p].to==last) continue;
        gs(bn[p].to,now);
        size[now]+=size[bn[p].to];
    }
}

P gr(int now,int last,int tot)
{
    int p,q;
    P res=mp(tot,0);
    for(p=first[now]; p!=-1; p=bn[p].next)
    {
        if(vis[p>>1] || bn[p].to==last) continue;
        res=min(res,gr(bn[p].to,now,tot));
        res=min(res,mp((int)abs(tot-size[bn[p].to]*2),(int)(p>>1)));
    }
    return res;
}

你可能感兴趣的:(树,算法)