说明
树分治的一种,与点分相似,每次找到两边节点数量相对接近的一条边(与重心相似),然后考虑经过这条边的路径,之后在对边的两边分别考虑。
对于菊花图,如果直接边分,那么复杂度显然会由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; j<son[i].size(); j++)
{
ad(i,son[i][j],qn[i][j]);
}
}
else
{
p=++n,q=++n;
for(j=0; j<son[i].size(); j++)
{
if(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;
}
—