zoukankan      html  css  js  c++  java
  • 虚树学习笔记

    前言

    前几天做比赛遇到一道题,考场想到了虚树,但是真正实现起来发现不太会打,所以回去复习了一下虚树。

    然而那道题正解并不是虚树。

    正文

    以【SDOI2011】消耗战 为例。

    这道题是很经典的虚树模板,暴力做法很简单,树形dp即可。

    但是容易发现,有非常多的点实际上是不需要的,这些都不是特殊点,同时对答案没有影响。

    考虑一种建树方式,只包括必经点,在这颗树上进行dp。也就是虚树。

    容易发现,只有这些特殊点以及这些点之间的(lca)是必经点,这些必经点的总数是非常有限的,考虑如何找出这些必经点。

    将所有点按(dfs)序排序,然后将两两间的lca加入点集,用哈希维护,这是一个非常显然而简单的做法。

    考虑更好打而且常数更小更快的做法。

    维护一个栈,栈中的点表示一条从根出发的路径。按(dfs)序排序之后,一次将每个点入栈,如果发现这个点可以接在链上就直接插入,如果不行要考虑弹栈。也就是将这条链以外的点弹出。

    每次判断当前点与栈顶点的(lca)与栈顶的上一个点的 (dfs)序的大小。因为这两个点都在栈顶点到根的路径上,所以如果比上一个点的(dfs)序小,说明上一个点不在这条新的链上,可以弹出上一个点,并将其加入点集;如果比上一个点大,说明上一个点在路径上,所以将(lca)和当前点入栈并加入点集即可。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define N 500010
    #define K 20
    #define ll long long
    using namespace std;
    int n,Q,x,y,w,m,num,i,j,k[N],dfn[N],q[N],dep[N],bz[N],tot,top,stk[N],lca,qv[N],totv,pl,fa[N][K],mn[N][K];
    ll dp[N];
    struct edge{
    	int to,next,len;
    }e[N],ev[N];
    int read(){
    	int x=0;
    	char ch=getchar();
    	while (ch<'0'||ch>'9') ch=getchar();
    	while (ch>='0'&&ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x;
    }
    void insert_edge(int x,int y,int w){
    	tot++;
    	e[tot].to=y;
    	e[tot].next=q[x];
    	q[x]=tot;
    	e[tot].len=w;
    }
    void insertv_edge(int x,int y,int w){
    	totv++;
    	ev[totv].to=y;
    	ev[totv].next=qv[x];
    	qv[x]=totv;
    	ev[totv].len=w;
    }
    void dfs(int x,int father){
    	int i,y;
    	fa[x][0]=father;
    	for (i=1;i<K;i++)
    		fa[x][i]=fa[fa[x][i-1]][i-1],mn[x][i]=min(mn[x][i-1],mn[fa[x][i-1]][i-1]);
    	dep[x]=dep[father]+1;
    	dfn[x]=++num;
    	for (i=q[x];i;i=e[i].next){
    		y=e[i].to;
    		if (y!=father)
    			mn[y][0]=e[i].len,dfs(y,x);
    	}
    }
    int cmp(int x,int y){
    	return dfn[x]<dfn[y];
    }
    int getlca(int x,int y){
    	int i;
    	if (dep[x]<dep[y]) swap(x,y);
    	for (i=K-1;i>=0;i--) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
    	if (x==y) return x;
    	for (i=K-1;i>=0;i--) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    	return fa[x][0];
    }
    int getmin(int x,int y){
    	int i,res=1e9;
    	if (x==y) return 0;
    	for (i=K-1;i>=0;i--) if (fa[x][i]!=y) res=min(mn[x][i],res),x=fa[x][i];
    	return res;
    }
    void dfs_ans(int x,int father,int ver){
    	int i,y;
    	dp[x]=0;
    	for (i=qv[x];i;i=ev[i].next){
    		y=ev[i].to;
    		dfs_ans(y,x,ver);
    		if (bz[y]==ver) dp[x]+=ev[i].len;
    		else dp[x]+=min((ll)ev[i].len,dp[y]);
    	}
    }
    int main(){
    	freopen("virtual_tree.in","r",stdin);
    	freopen("virtual_tree.out","w",stdout);
    	n=read();
    	for (i=1;i<n;i++){
    		x=read();y=read();w=read();
    		insert_edge(x,y,w);
    		insert_edge(y,x,w);
    	}
    	tot=num=0;
    	memset(mn,127,sizeof(mn));
    	dfs(1,0);
    	Q=read();
    	while (Q--){
    		num=read();
    		for (i=1;i<=num;i++) k[i]=read();
    		top=1;stk[top]=1;
    		sort(k+1,k+num+1,cmp);
    		pl=1;
    		qv[1]=0;
    		totv=0;
    		for (i=1;i<=num;i++){
    			lca=getlca(k[i],stk[top]);
    			bz[k[i]]=Q;
    			if (lca==stk[top]) stk[++top]=k[i],qv[k[i]]=0;
    			else{
    				while (dfn[lca]<dfn[stk[top-1]]&&top>1){
    					insertv_edge(stk[top-1],stk[top],getmin(stk[top],stk[top-1]));
    					top--;
    				}
    				if (dfn[lca]>dfn[stk[top-1]]){
    					qv[lca]=0;
    					insertv_edge(lca,stk[top],getmin(stk[top],lca));
    					top--;
    					stk[++top]=lca;
    				}
    				else{
    					insertv_edge(lca,stk[top],getmin(stk[top],lca));
    					top--;
    				}
    				stk[++top]=k[i];
    				qv[k[i]]=0;
    			}
    		}
    		while (top>1){
    			insertv_edge(stk[top-1],stk[top],getmin(stk[top],stk[top-1]));
    			top--;
    		}
    		dfs_ans(1,0,Q);
    		printf("%lld
    ",dp[1]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    BigDecimal 类型数据比较大小
    list排序,顺序,倒序
    springboot添加log4j日志配置log4j.xml生成日志文件
    mybatis使用@param("xxx")注解传参和不使用的区别
    jetty 插件启动指定端口号
    javaweb项目静态资源被拦截的解决方法
    day15 Python全局变量和局部变量
    阿里云操作视频
    Python基础视频
    马哥Python视频
  • 原文地址:https://www.cnblogs.com/Mohogany/p/14044796.html
Copyright © 2011-2022 走看看