虚树

建立虚树

先把关键点按\(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; 

}

应用

指定贡献点

给出若干组点,每组只有这些点产生贡献

点击查看代码

posted @ 2024-11-11 10:48  Zhone_lb  阅读(16)  评论(0)    收藏  举报