建立虚树
先把关键点按\(dfn\)升序排序,单调栈维护树上一条链(\(dfn\)单增),初始为根节点\(1\)
逐个考虑关键点\(x\):
设\(lca=LCA(x,st[top])\),分类讨论:
\(\hspace{0.5cm}\)①若\(lca=st[top]\),表明\(x\)和\(st[]\)同一条链上,直接入栈;
\(\hspace{0.5cm}\)②若\(lca\not=st[top]\),则\(lca\)一定在\(st[top]\)的上方,即子树\(st[top]\)内的关键点已访问完毕,尝试弹出\(st[top]\),继续分类讨论:
\(\hspace{1cm}\)①若\(dfn[lca]>dfn[st[top-1]]\),表明\(lca\)在\(st[top]\)和\(st[top-1]\)中间,将\(lca\)与\(st[top]\)连边,弹出\(st[top]\),再将\(lca\)加入;
\(\hspace{1cm}\)②若\(dfn[lca]\le dfn[st[top-1]]\),表明\(st[top]\)与\(st[top-1]\)直接相连(可以证明没有另外的\(lca'\)在中间),将\(st[top-1]\)与\(st[top]\)连边,弹出\(st[top]\)
结束分类讨论,考虑下一个点
细节
实质上就是个分类讨论:\(lca\)和\(st[top]\)的关系,和\(lca\)和\(st[top-1]\)的关系,像splay一样画个图就想得通了
在加边时建议这样写——先连\(son\to fa\),再补成双边,因为儿子到父亲只有一条边,这时清空$head[]
$最方便,否则要么清多了T掉,要么把刚连上的边清了
void Build2() {
stpos=NG.tot=NG.head[1]=0;
st[++stpos]=1;
for(int i=1,lca;i<=kpos;i++) {
while((lca=LCA(knod[i],st[stpos]))!=st[stpos]) {
NG.head[st[stpos]]=0;
if(dfn[lca]>dfn[st[stpos-1]]) {
NG.Addedge(st[stpos],lca,Mind(st[stpos],lca));
st[stpos]=lca;
}
else {
NG.Addedge(st[stpos],st[stpos-1],Mind(st[stpos],st[stpos-1]));
--stpos;
}
}
st[++stpos]=knod[i];
}
while(stpos>1) {
NG.head[st[stpos]]=0;
NG.Addedge(st[stpos],st[stpos-1],Mind(st[stpos],st[stpos-1]));
--stpos;
}
for(int i=NG.tot;i;i--) NG.Addedge(NG.e[i].to,NG.e[i].fr,NG.e[i].dis);//,cout<<NG.e[i].to<<' '<<NG.e[i].fr<<' '<<NG.e[i].dis<<endl;
}
应用
指定贡献点
给出若干组点,每组只有这些点产生贡献
点击查看代码