把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

虚树学习笔记

题面传送门
虚树,顾名思义是虚的,就是不能完全代表一棵树的树。
换句话说,虚树上只保留对我们有用的节点。而其它节点就没有必要存在。
可以来看一道例题[SDOI2011]消耗战
我们很容易发现,如果只保留树上的资源点和资源点两两之间的lca,那么其余的点全部是没有用的。
同时我们又知道结论:\(n\)个点两两的\(lca\)只有最多\(n-1\)个点。
所以这样子建出来的虚树一定是可以的。
说的好听,这怎么建啊
我们先将所有资源点按dfs序升序排序,然后依次插入树中。
具体的,维护一个dfs序升序的栈,这个栈表示虚树最右边的一条链。因为我插入的点dfs序递增,我只需要知道最右边的信息即可。
然后设栈顶为\(top\),栈顶的下面一个是\(y\),待插入的点为\(x\),\(lcas=lca(top,x)\)那么有分类讨论:
如果\(d[lca]<d[y]\),即我不接着这条链走,我要换一条链走,所以不断弹出栈与连边直至这个条件不满足。
然后这时又有情况:
如果此时的\(lca=top\),那么什么都不用做。
如果不等于,那么\(d_top\)一定小于\(d_lca\),而这时又是换一边走,所以把连边然后\(top\)弹出然后看看\(lcas\)是否为\(y\)然后看是否加入即可。
最后把\(x\)加入,别忘了最后把栈中的边加入虚树。
最后树形\(dp\)即可。
时间复杂度\(O(mlogn+\sum\limits{k}logn)\)
code:

#include<cstdio>
#define ll long long
#include<algorithm>
#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,st[250039],now,lcas,pus,sh,dh,flag[250039],a[250039],d[250039],top[250039],siz[250039],son[250039],fa[250039],dfn[250039];
ll w[250039],dp[250039];
struct yyy{int to,w,z;};
struct ljb{
	int head,h[250039];yyy f[500039];
	inline void add(int x,int y,int z){f[++head]=(yyy){y,z,h[x]};h[x]=head;}
}s,g;
inline void dfs1(int x,int last){
	d[x]=d[last]+1;siz[x]=1;yyy tmp;fa[x]=last;dfn[x]=++dh;
 	for(beg(x);end;go) tmp=s.f[cur],(tmp.to^last)&&(w[tmp.to]=min(w[x],tmp.w),dfs1(tmp.to,x),siz[x]+=siz[tmp.to],son[x]=siz[son[x]]>siz[tmp.to]?son[x]:tmp.to);
}
inline void dfs2(int x,int last){
	top[x]=last;if(!son[x]) return;dfs2(son[x],last);yyy tmp;
	for(beg(x);end;go) tmp=s.f[cur],(tmp.to^fa[x]&&tmp.to^son[x])&&(dfs2(tmp.to,tmp.to),1); 
}
inline int lca(int x,int y){while(top[x]!=top[y]) d[top[x]]>d[top[y]]?x=fa[top[x]]:y=fa[top[y]];return d[x]>d[y]?y:x;}
inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
inline void dfs(int x){
	dp[x]=w[x];ll pus=flag[x]*1e18;yyy tmp;
	for(int cur=g.h[x];end;go)tmp=g.f[cur],dfs(tmp.to),pus+=dp[tmp.to];dp[x]=min(dp[x],pus); g.h[x]=0;
}
int main(){
	freopen("1.in","r",stdin);
	register int i,j;
	scanf("%d",&n);w[1]=1e18;
	for(i=1;i<n;i++) scanf("%d%d%d",&x,&y,&z),s.add(x,y,z),s.add(y,x,z);dfs1(1,0);dfs2(1,1);
	for(scanf("%d",&m),i=1;i<=m;i++,g.head=0){
		scanf("%d",&k);for(j=1;j<=k;j++) scanf("%d",&a[j]),flag[a[j]]=1;sort(a+1,a+k+1,cmp);st[sh=1]=1;
		for(j=1;j<=k;j++){
			now=a[j];lcas=lca(st[sh],now);
			while(sh&&dfn[lcas]<dfn[st[sh-1]])g.add(st[sh-1],st[sh],0),sh--;lcas=lca(st[sh],now);
			if(lcas!=st[sh])g.add(lcas,st[sh],0),lcas==st[sh-1]?sh--:st[sh]=lcas;st[++sh]=now;
		}
		while(--sh)g.add(st[sh],st[sh+1],0);dfs(1);
		printf("%lld\n",dp[1]); for(j=1;j<=k;j++) flag[a[j]]=0;
	}
}
posted @ 2021-02-22 15:43  275307894a  阅读(56)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end