题目大意

给你一个NN个节点的树,现在有QQ组询问,对于第ii组询问先给你一个MiM_i个树中的节点,设这些节点为关键点,对于树中的每个点都属于它最近的关键点,如有多个则选编号最小的那个。要求找出每个关键点包含了多少个树中的节点(包括自己)。

N≤300000N \leq 300000
Q≤3000000Q \leq 3000000
∑Mi≤300000\sum_{M_i} \leq 300000

解体思路

一看到题目,N,MN,M那么大,而∑Mi≤300000\sum_{M_i} \leq 300000,那么就知道这题的解法肯定跟MM有关。

为了减少树中节点的个数,虚树就是个能满足这个要求的数据结构,对于这题,我们需要考虑的就只有那MM个点和它们两两见的LcaLca,而LcaLca的个数不会超过MM个,那么假如虚树上的点都是关键点,那么们只需在虚树相邻的两个点的路径上找到一个分界点(这里指原树的路径,其实只需要记录一下深度就可以了),使分界点两边的点分配到这两个关键点中,这当然很简单,并且能线性的完成。可问题是,并不是虚树上的每个点都是关键点,还有关键点的Lca。

那么我们把虚树中的关键点成为黑点,其它称为白点。对于每个白点,我们需要先找出一个离它最近的黑点,作为控制它的点,这个可以用树形dp上下各扫一遍线性的完成。那我们考虑一下虚树上的每条边:

连接两个黑点:直接找分界点即可。

连接一个黑点,一个白点:如果控制这个白点的就是这个黑点,显然这条边上的所有点都属与这个黑点。如果不是那我们就求控制这个白点的黑点和这个黑点之间路径的分界点,显然这个分界点肯定在这条边上,那找到分界点后直接分配边上的点就可以了。

连接两个白点:找到控制这两个白点的黑点,那么就跟第二种情况一样了。

至此我们就有了线性求出每个关键点包含的点了,并且是与MiM_i相关,那么这题就完美解决了。

程序

//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>using namespace std;const int MAXN = 3e5 + 5;int tot, Last[MAXN], Next[MAXN * 2], Go[MAXN * 2];
int N, Q, Time, L[MAXN], R[MAXN], Close[MAXN], tag[MAXN], Flag[MAXN], Num[MAXN], Clear[MAXN];
int Fa[MAXN][21], Deep[MAXN], Size[MAXN], Dist[MAXN], D[MAXN], P[MAXN], Pre[MAXN], Ans[MAXN];bool Cmp(int A, int B) { return L[A] < L[B];}void Link(int u, int v) {Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v;
}void Init() {scanf("%d", &N);for (int i = 1; i < N; i ++) {int u, v;scanf("%d%d", &u, &v);Link(u, v), Link(v, u);}
}void Dfs(int Now, int Pre) {L[Now] = ++ Time, Fa[Now][0] = Pre;Size[Now] = 1, Deep[Now] = Deep[Pre] + 1;for (int p = Last[Now]; p; p = Next[p]) {int v = Go[p];if (v == Pre) continue;Dfs(v, Now);Size[Now] += Size[v];}R[Now] = Time;
}void PreFa() {for (int j = 1; j <= 20; j ++)for (int i = 1; i <= N; i ++)Fa[i][j] = Fa[Fa[i][j - 1]][j - 1];
}int GetPre(int Now, int Goal) {for (int i = 20; i + 1; i --) if (Deep[Fa[Now][i]] >= Goal) Now = Fa[Now][i];return Now;
}int Lca(int u, int v) {if (Deep[u] < Deep[v]) swap(u, v);for (int i = 20; i + 1; i --) if (Deep[Fa[u][i]] >= Deep[v]) u = Fa[u][i];if (u == v) return u;for (int i = 20; i + 1; i --) if (Fa[u][i] != Fa[v][i]) u = Fa[u][i], v = Fa[v][i];return Fa[u][0];
}int Dis(int A, int B) {return Deep[A] + Deep[B] - Deep[Lca(A, B)] * 2;
}void Update(int Now, int From, int Ask) {From = Close[From];int dis = Dis(Now, From);if (tag[Now] != Ask || Dist[Now] > dis || Dist[Now] == dis && Close[Now] > From) {tag[Now] = Ask;Close[Now] = From;Dist[Now] = Dis(Now, From);}
}void PreDown(int Ask, int New) {for (int i = 2; i <= New; i ++) Update(P[i], Pre[P[i]], Ask);
}void PreUp(int Ask, int New) {for (int i = New; i; i --) {int Now = P[i];for (int p = Last[Now]; p; p = Next[p]) Update(Now, Go[p], Ask);}
}void Solve(int Id1, int Id2, int Side1, int Side2, int Below) {Num[Id2] += 1;if (Fa[Side2][0] == Side1) return;int Len = Dis(Id1, Id2) - 1;int Mid = GetPre(Side2, Deep[Id2] - (Len + 1) / 2);if (Len & 1 && Id2 > Id1) Mid = GetPre(Side2, Deep[Mid] + 1); Num[Id1] += Size[Below] - Size[Mid];Num[Id2] += Size[Mid] - Size[Side2];
}void GetAns(int Now, int Ask) {int Son = 0;for (int p = Last[Now]; p; p = Next[p]) {int v = Go[p];if (Clear[Close[v]] != Ask) Num[Close[v]] = 0, Clear[Close[v]] = Ask;int Below = GetPre(v, Deep[Now] + 1);Son += Size[Below];if (Close[Now] == Close[v]) {Num[Close[Now]] += Size[Below] - Size[v] + 1;} else Solve(Close[Now], Close[v], Now, v, Below);GetAns(Go[p], Ask);}Num[Close[Now]] += (Size[Now] - Son - 1);
}void Solve() {Dfs(1, 0);PreFa();scanf("%d", &Q);for (int Ask = 1; Ask <= Q; Ask ++) {int M;memset(Last, 0, sizeof Last);tot = 0;scanf("%d", &M);for (int i = 1; i <= M; i ++) {scanf("%d", &P[i]);Ans[i] = P[i];Flag[P[i]] = Ask;Close[P[i]] = P[i], tag[P[i]] = Ask, Dist[P[i]] = 0;}sort(P + 1, P + 1 + M, Cmp);int All = M, New = 0;   for (int i = 1; i <= M - 1; i ++) P[++ All] = Lca(P[i], P[i + 1]);sort(P + 1, P + All + 1, Cmp);for (int i = 1; i <= All; i ++) if (P[i] != P[i - 1]) P[++ New] = P[i];D[0] = 0;for (int i = 1; i <= New; i ++) {int Now = P[i];Last[Now] = 0;while (D[0] && (R[Now] < L[D[D[0]]] || L[Now] > R[D[D[0]]])) -- D[0];if (D[0] > 0) Link(D[D[0]], Now), Pre[Now] = D[D[0]];D[++ D[0]] = Now;}PreUp(Ask, New);PreDown(Ask, New);Num[Close[P[1]]] = Size[1] - Size[P[1]] + 1, Clear[Close[P[1]]] = Ask;GetAns(P[1], Ask);for (int i = 1; i <= M; i ++) printf("%d ", Num[Ans[i]]);printf("\n");}
}int main() {freopen("worldtree.in", "r", stdin), freopen("worldtree.out", "w", stdout);Init();Solve();
}

HNOI2014 世界树 基于虚树的树形动态规划相关推荐

  1. BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】

    题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...

  2. 青云的机房组网方案(简单+普通+困难)(虚树+树形DP+容斥)

    题目链接 1.对于简单的版本n<=500, ai<=50 直接暴力枚举两个点x,y,dfs求x与y的距离. 2.对于普通难度n<=10000,ai<=500 普通难度解法挺多 ...

  3. P3233-[HNOI2014]世界树【虚树,倍增】

    正题 题目链接:https://www.luogu.com.cn/problem/P3233 题目大意 nnn个点的一棵树,mmm次选出一些点作为关键点.每个树上的点会对最近的关键点做贡献,求每个关键 ...

  4. 【HNOI2014】世界树(worldtree) (虚树详解)

    题目大意 原题又臭又长,总之简介来说的话就是 给定一棵树,有若干个询问,每次给定m个点,每个点都被这m个点中最近(距离相同,编号小的近)的点管辖.问m个点分别管几个点. n<=300000,q& ...

  5. 浅谈虚树(虚仙人掌)

    虚树是什么? 在 OI 比赛中,有这样一类题目:给定一棵树,另有多次询问,每个询问给定一些关键点,需要求这些关键点之间的某些信息.询问数可能很多,但满足所有询问中关键点数量的总和比较小. 由于询问数可 ...

  6. 【Learning】虚树题目汇总

    这里主要是我虚树的练习记录. 关于虚树的建树,参考了 https://www.cnblogs.com/chenhuan001/p/5639482.html .感谢!至于为什么虚树建树要打那么长,只是为 ...

  7. 虚树(bzoj 3572: [Hnoi2014]世界树)

    例题: 一棵n个节点的树,m次查询,每次查询给你一个点集U,对于树上的所有节点x(x∉U),你要找到一个点y(y∈U)满足y点离x点最近且标号最小,表示x点受y点管辖,而你的任务就是对于每次查询输出U ...

  8. 「HNOI2014」世界树 虚树

    「HNOI2014」世界树 前置技能:虚树. (本题可以通过以下相似的思想用线段树维护子树信息和倍增找中点完成,代码短很多,但本篇题解不涉及) 题解部分 这种总询问点数不大,但是询问次数多,可以想到用 ...

  9. 「洛谷2495」「BZOJ3052」「SDOI2001」消耗战【虚树+树形动态规划】

    题目大意 给你\(k\)个点,让这一些点和一号节点断开,删去某一些边,求最小的删去边权之和. 做题的心路历程 做了\(HG\)昨天的模拟赛,深深感觉到了窝的菜,所以为了\(A\)掉T1这一道毒瘤,窝就 ...

  10. bzoj3572 [HNOI2014]世界树 虚树 +乱dp

    这个题有Σ的条件,肯定还是用log结构求询问点相关了 但这个题是点之间的距离关系,所以本来想用中点来代替原来的lca,但中点的个数不满足任何单调性,而且个数也不是n个 所以还是要用lca,所以考虑lc ...

最新文章

  1. Linux 下 RMAN无反应问题处理
  2. WPF对决Silverlight:为项目选择最佳技术
  3. [工具库]JFileDownloader工具类——多线程下载网络文件,并保存在本地
  4. [jQuery] jQuery是如何链式调用的?
  5. strlen函数在哪个头文件_第二十七中、字符串处理函数-strlen、strcat、strncat
  6. 什么样的环境才是最理想的工作环境呢?
  7. Silverlight学习笔记四BusyIndicator控件(进度条)
  8. java来电报名字的软件_教你一招,手机来电话可以语音报出来电人的姓名和电话号码,收藏...
  9. 网际风全推数据接口_网际风数据接口飞狐交易师版简要说明.doc
  10. 微信小程序微信授权登录
  11. 应用程序“xxx“不能打开,怎么解决?
  12. matlab的grid on,Matlab基本函数-grid、box函数 | 学步园
  13. SAP中成本核算结构及构成组件分析
  14. MySQL导入数据遇到Error Number: 1467 Failed to read auto-increment value from storage engine错误
  15. 数学Ⅰ基础复习(六)
  16. xlsx表格怎么筛选重复数据_excel表格如何过滤筛选重复项内容
  17. python中and和or的惰性求值特点_Python 惰性求值
  18. ERP与MBA的关系
  19. APP Designer 制作简易英汉词典的回调函数书写
  20. python中使用virtualenv库创建虚拟环境的问题

热门文章

  1. Halcon例程分析2:颜色检测识别
  2. 动态表格的实现(layui动态表格实现)
  3. NOIP2015酱油记
  4. IKEv2子网之间秘钥重协商
  5. (第二章)HTML基本标记
  6. 私域流量分析之李子柒
  7. eclipse php集成包,php-eclipse集成配置(Ecli
  8. 读书心得:一个程序员的自我反思
  9. 参考文献中文刊名的英文缩写对照表
  10. xheditor编辑器的使用