传送门

虚树入门题?

好难啊。

在学习别人的写法之后终于过了。

这道题dp方程很好想。

主要是不好写。

简要说说思路吧。

显然最优值只能够从子树和父亲转移过来。

于是我们先dfs一遍用儿子更新父亲,然后再dfs一遍用父亲更新儿子。

这样搞完之后可以统计出每个点所属的管辖点。

然后统计。

但这样单次跑是O(n)O(n)O(n)的不优秀。

考虑优化算法的时间复杂度。

注意到所有管辖点加起来只有O(n)O(n)O(n)个。

因此我们每次只把跟管辖点有关的点连起来建出一棵虚树。

然后每次就在上面跑带边权的dp。

最后顺便用倍增统计对答案的贡献。

这样每次在虚树上面dp的时间复杂度是O(O(O(虚树大小)))的。

于是就可以过啦。

最后提一提(贴一发)建虚树的步骤。

  1. 输入每个询问的点,并且按照dfs序为关键字排序
  2. 将第1个点压到栈当中,开始构建虚树
  3. 枚举到下一个点u,计算u与栈顶点v的公共祖先lca
  4. 假设栈中栈顶下方的点为w(若栈中只有1个点就直跳过这一步),若w点的深度大于lca就把v向w连一条边,并且弹掉v,重复此步,否则就到下一步
  5. 若lca不是当前的v,那么就把lca和v连边,把v弹出,让lca成为栈顶元素(注:这个操作的意思是如果栈顶没有这个lca那么就压入),否则不做任何操作
  6. 最后把u压入栈中
  7. 回到3操作枚举下个点,直到枚举完了所有点
  8. 把栈顶v与栈顶下方的点为w连边,并且把v弹掉,这么做直到栈里只有一个点
  9. 栈里剩下的点就是虚树的根了

    接下来你就可以开始进行dp等操作了

    最后我再提一个比较优秀的细节。

    我们最开始可以直接把1号点放入虚树,这样方便的多,而不用像上面说的把最后栈顶的点作为树根。

    代码:
#include<bits/stdc++.h>
#define N 300005
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
inline void write(int x){
	if(x>9)write(x/10);
	putchar(x%10^48);
}
int n,rt,q,m,first[N],b[N],a[N],st[N][21],cal[N],ans[N],siz[N],dfn[N],g[N],bel[N],dep[N],stk[N],cnt=0,dfn_cnt=0,top=0,tot=0;
struct egde{int v,next;}e[N<<1];
inline void add(int u,int v){if(!u||u==v)return;e[++cnt].v=v,e[cnt].next=first[u],first[u]=cnt;}
inline void dfs(int p){
	siz[p]=1,dfn[p]=++dfn_cnt;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==st[p][0])continue;
		st[v][0]=p,dep[v]=dep[p]+1,dfs(v),siz[p]+=siz[v];
	}
}
inline int lca(int x,int y){
	if(dep[x]<dep[y])x^=y,y^=x,x^=y;
	for(int i=20;~i;--i)if(dep[x]-(1<<i)>=dep[y])x=st[x][i];
	if(x==y)return x;
	for(int i=20;~i;--i)if(st[x][i]!=st[y][i])x=st[x][i],y=st[y][i];
	return st[x][0];
}
inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
inline int calc(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
inline void dfs1(int p,int fa){
	int dis1,dis2;
	g[p]=siz[p],cal[++tot]=p;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==fa)continue;
		dfs1(v,p);
		if(!bel[v])continue;
		if(!bel[p]){bel[p]=bel[v];continue;}
		dis1=calc(bel[v],p),dis2=calc(bel[p],p);
		if(dis1<dis2||(dis1==dis2&&bel[v]<bel[p]))bel[p]=bel[v];
	}
}
inline void dfs2(int p,int fa){
	int dis1,dis2;
	for(int i=first[p];i;i=e[i].next){
		int v=e[i].v;
		if(v==fa)continue;
		if(!bel[v])bel[v]=bel[p];
		else{
			dis1=calc(v,bel[v]),dis2=calc(bel[p],v);
			if(dis1>dis2||(dis1==dis2&&bel[v]>bel[p]))bel[v]=bel[p];
		}
		dfs2(v,p);
	}
}
inline void solve(int u,int v){
	int son=v,mid=v,Next,dis1,dis2;
	for(int i=20;~i;--i)if(dep[son]-(1<<i)>dep[u])son=st[son][i];
	g[u]-=siz[son];
	if(bel[u]==bel[v]){ans[bel[u]]+=siz[son]-siz[v];return;}
	for(int i=20;~i;--i){
		Next=st[mid][i];
		if(dep[Next]<=dep[u])continue;
		dis1=calc(Next,bel[u]),dis2=calc(Next,bel[v]);
		if(dis1>dis2||(dis1==dis2&&bel[u]>bel[v]))mid=Next;
	}
	ans[bel[u]]+=siz[son]-siz[mid],ans[bel[v]]+=siz[mid]-siz[v];
}
int main(){
	n=read();
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	dfs(1),cnt=0,memset(first,0,sizeof(first));
	for(int j=1;j<=20;++j)for(int i=1;i<=n;++i)st[i][j]=st[st[i][j-1]][j-1];
	q=read();
	while(q--){
		m=read();
		for(int i=1;i<=m;++i)a[i]=b[i]=read(),bel[a[i]]=a[i];
		top=cnt=tot=0,sort(a+1,a+m+1,cmp);
		if(bel[1]!=1)stk[++top]=1;
		int t;
		for(int i=1;i<=m;++i){
			if(!top){stk[++top]=a[i];continue;}
			t=lca(stk[top],a[i]);
			while(1){
				if(dep[stk[top-1]]<=dep[t]){
					add(t,stk[top]),--top;
					if(stk[top]!=t)stk[++top]=t;
					break;
				}
				add(stk[top-1],stk[top]),--top;
			}
			if(stk[top]!=a[i])stk[++top]=a[i];
		}
		while(top>1)add(stk[top-1],stk[top]),--top;
		--top,dfs1(1,0),dfs2(1,0);
		for(int i=1;i<=tot;++i)for(int j=first[cal[i]];j;j=e[j].next)solve(cal[i],e[j].v);
		for(int i=1;i<=tot;++i)ans[bel[cal[i]]]+=g[cal[i]];
		for(int i=1;i<=m;++i)printf("%d ",ans[b[i]]);
		puts("");
		for(int i=1;i<=tot;++i)ans[cal[i]]=first[cal[i]]=g[cal[i]]=bel[cal[i]]=0;
	}
	return 0;
}

2018.09.25 bzoj3572: [Hnoi2014]世界树(虚树+树形dp)的更多相关文章

  1. 【BZOJ-3572】世界树 虚树 + 树形DP

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1084  Solved: 611[Submit][Status ...

  2. BZOJ 3572: [Hnoi2014]世界树 虚树 树形dp

    https://www.lydsy.com/JudgeOnline/problem.php?id=3572 http://hzwer.com/6804.html 写的时候参考了hzwer的代码,不会写 ...

  3. bzoj3572[Hnoi2014] 世界树 虚树+dp+倍增

    [Hnoi2014]世界树 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1921  Solved: 1019[Submit][Status][Dis ...

  4. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  5. bzoj 2286(虚树+树形dp) 虚树模板

    树链求并又不会写,学了一发虚树,再也不虚啦~ 2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5002  Sol ...

  6. BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP+树剖lca

    BZOJ_2286_[Sdoi2011]消耗战_虚树+树形DP Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的 ...

  7. 洛谷 P3233 [HNOI2014]世界树(虚树+dp)

    题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...

  8. BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP

    题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...

  9. [WC2018]通道——边分治+虚树+树形DP

    题目链接: [WC2018]通道 题目大意:给出三棵n个节点结构不同的树,边有边权,要求找出一个点对(a,b)使三棵树上这两点的路径权值和最大,一条路径权值为路径上所有边的边权和. 我们按照部分分逐个 ...

随机推荐

  1. leetcode412

    public class Solution { public IList<string> FizzBuzz(int n) { var list = new List<string&g ...

  2. ajax 实现跨域

    ajax本身是不可以跨域的,通过产生一个script标签来实现跨域.因为script标签的src属性是没有跨域的限制的. 其实设置了dataType: 'jsonp'后,$.ajax方法就和ajax ...

  3. fb 更新sdk

    flash兼容flex.fb的sdk,但fb不一定兼容flash的sdk,那么直接将flash的sdk解压覆盖掉fb的sdk,就可以打开了. fb更新sdk方法: 1.找到(安装目录+eclipse\ ...

  4. 五种方法实现Java的Singleton单例模式

    面试的时候经常会问到Java的单例模式,这道题能很好的考察候选人对知识点的理解程度.单例模式要求在系统运行时,只存在唯一的一个实例对象. 下面我们来详细剖析一下其中的关键知识点,并介绍五种实现方法,以 ...

  5. 压缩文件tar.gz和zip之间的区别

    我们在开发的时候通常要先下载相关的软件或者是源码,或者是jar包.在下载东西的时候总是碰见后缀是.tar.gz和.zip的问题,搞不清楚是怎么回事,不晓得下载哪个文件才是对自己有用的.现在我知道了,其 ...

  6. requireJS-初识

    浅谈requireJS 2016-04-26 21:44 by 猴子猿, 726 阅读, 0 评论, 收藏, 编辑 项目中大都使用模块化开发,requireJS作为AMD模块开发的典范,所以有必要学习 ...

  7. mysql insert on duplicate key, update, ignore

    insert 语句中不能使用where,所以如果需要根据插入的数据在已有的数据库表是否重复做一些操作可以使用下面三种方法: 1. 使用insert,捕获duplicate错误 2. insert in ...

  8. The 2018 Nobel prizesThe Nobel prize for economics is awarded for work on the climate and economic growth

    The 2018 Nobel prizesThe Nobel prize for economics is awarded for work on the climate and economic g ...

  9. springboot 使用的配置

    1,控制台打印sql logging: level: com.sdyy.test.mapper: debug 2,开启驼峰命名 mybatis.configuration.map-underscore ...

  10. RESTORE 无法处理数据库 'Students',因为它正由此会话使用。建议在执行此操作时使用 master 数据库。

    恢复数据库是总弹出报错对话框如下:RESTORE 无法处理数据库 'Students',因为它正由此会话使用.建议在执行此操作时使用 master 数据库.RESTORE DATABASE 正在异常终 ...