【HNOI2014】世界树(worldtree) (虚树详解)
题目大意
原题又臭又长,总之简介来说的话就是
给定一棵树,有若干个询问,每次给定m个点,每个点都被这m个点中最近(距离相同,编号小的近)的点管辖。问m个点分别管几个点。
n<=300000,q<=300000,∑m<=300000。
The Solution
我们先来对题目进行分析,很明显,我们要往树上来想,那么怎么想呢?第一想法肯定是暴力啊!!可是,给定m个点,询问它分别管辖几个点,会有很多中状态,且都是可变的,这TM怎么暴力?
或许可能可以YY到处理它lca及自己的状态,然后再在树上贪心之类的~囧~
好主意估计能行,水点分吧,可这也很GG啊
好吧口胡了这么久,接下来切入主题
正解是虚树和树形DP
那么问题来了
什么是虚树呢?
(这里引用了别人的一些资料与图,供大家学习。)
假设如下的一棵树
蓝色的是询问点。红色点就会在虚树上。
我们用一个栈来维护虚树的“当前这一坨东西”…例如我们在栈中加入了18。然后接下来打算加入一个16。
我们现在发现这个栈顶的lca,也就是2,是有用的,那么我们现在就要把18弹掉,换成2,然后再扔进去一个16。
接下来我们要加一个20,那么它与栈顶的lca为2。我们就考虑16,16是没用的,把它弹掉,然后看见2,正好就是lca,就保留。
类似这样我们可以发现开始把所有询问点加入虚树后,我们把询问点按dfs序排个序,这时栈里面应该维护一个奇怪的玩意,先计算一个新加的点与栈顶的lca,然后如果一个栈里的东西一直都“没用”,也就是深度比这个lca来得大,就一直弹出栈顶,最后如果栈顶不是lca,就把lca加入栈,并且加入虚树,然后再加入这个点。
这样我们就可以求出虚树啦,同时我们也可以得到虚树上每一个点的父亲节点。
接下来我们考虑如何求出虚树上每个点被哪个点控制。我们可以用一个 pair<int,int> pair来存它到控制点的距离和控制点,然后我们用每一个点更新它的父亲,再用每一个父亲更新它的孩子。注意到因为所有虚树上的点的lca也在虚树上,所以对于虚树上某一个点和某一个控制点,第一遍必然会更新到lca,第二遍必然会更新到这个点,所以这个做法是正确的。
然后我们要考虑虚树以外的点要怎么维护。
比如对于这样一个小树,红色的点在虚树上。
我们考虑虚树上的一条父子边,对应实际的树上就会是一坨东西。比如这棵树上2-5就对应4和6。
具体地说,对于一条父子边(f,x),f=fa[x],那么对应实际树上的就是f在x方向的这棵子树除掉x这棵子树。
例如2-5就表示4这棵子树除掉5这棵子树,就是4和6这两个点。
我们发现树上除了这些父子边,还有一些没有被计入统计的点,需要分别考虑,虚树根往上的都需要单独统计,例如1,还有像图中的3这样也不会被统计到。
注意到跟这些点最近的点必然和和这些点相连的点最近的是同一个点,比如1和3最近的都与2最近的一样。
现在,如果5与2最近的是同一个点,那么4和6必然也是这一个点。
否则我们可以发现,这是与深度有关的。比如2最近的点->2距离为p,5最近的点->5距离为q。
那么与控制5的点距离不超过(p+q+2到5距离)/2的点都应该选择5最近的点,那么深度就要>=dep[5]-(near_dis(2)+near_dis(5)+dis(2,5))/2+near_dis(5)。
只要从5开始往上跳到这个深度,计算一下就可以了。
需要注意的是,如果(p+q+2到5距离)是偶数的话,一定会有一个(些)点到2和5最近的点距离相同,那么这个(些)点应该选编号小的一个。即因为正常情况下这些点会选择下面那个点的控制点,所以如果上面那个点控制点编号小,就要考虑深度++。
所以基本的思路都清楚了,我们只要写一个倍增维护一下lca和往上跳这种东西就可以了。复杂度大概是O((n+q+∑m)logn)的。
参考CODE
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define INF 1 << 30
#define N 600005 using namespace std;int read(int &n)
{char ch = ' ';int q = 0, w = 1;for (;(ch != '-') && ((ch < '0') || (ch> '9'));ch = getchar());if (ch == '-') w = -1,ch = getchar();for (; ch >= '0' && ch <= '9';ch = getchar()) q = q * 10 + ch - 48;n = q * w;return n;
}namespace ib {char b[100];}inline void pint(int x)
{if (x == 0){putchar(48);return;}if (x < 0) {putchar('-');x = -x;}char *s = ib :: b;while (x) *(++ s) = x % 10,x /= 10;while (s != ib :: b) putchar((* (s --)) + 48);
}typedef long long ll;struct Edge
{int to,next;Edge(void){}Edge(int a,int b) : to(a),next(b){}
}E[N];int tot = 0,n,Q;
int Final[N];void Link(int x,int y)
{E[++ tot] = Edge(y,Final[x]),Final[x] = tot;E[++ tot] = Edge(x,Final[y]),Final[y] = tot;
}int Dad[N],Up[N][20],Dep[N],M=0,Dfsn[N],C=0,Son[N];void Dfs_rt(int x)
{Dfsn[x] = ++ C;Son[x] = 1;for (int k = Final[x];k;k = E[k].next){if (E[k].to == Dad[x]) continue;Dad[E[k].to] = Up[E[k].to][0] = x;Dep[E[k].to] = Dep[x] + 1;Dfs_rt(E[k].to);Son[x] += Son[E[k].to];}
}void Build_rt()
{Dfs_rt(1);fo(g,1,19)fo(i,1,n)if (Up[i][g - 1]) Up[i][g] = Up[Up[i][g - 1]][g - 1];
}
jump up (x=fa[x]) until dep[x]=d
int Jump(int x,int d)
{fd(i,19,0)if (!Up[x][i] || Dep[Up[x][i]] < d);else x = Up[x][i];return x;
}int Lca(int x,int y)
{if (Dep[x] > Dep[y]) swap(x,y);y = Jump(y,Dep[x]);if (x == y) return x;fd(i,19,0)if (Up[x][i] != Up[y][i]) x = Up[x][i],y = Up[y][i];return Dad[x];
}typedef pair<int,int> P;
bool cmp_Dfsn(int a,int b) { return Dfsn[a] < Dfsn[b];}
int ss[N],vs[N],st[N],vfa[N],emp[N],vfe[N],ans[N],ss_[N],vn,stn = 0,sn;
P Dot[N]; //(dis,controller)
//vs: points in vtree
void Build_vt()
{vn = stn = 0;fo(i,1,sn) ss_[i] = ss[i];sort(ss + 1,ss + 1 + sn,cmp_Dfsn);fo(i,1,sn) {vs[++ vn] = ss[i];Dot[ss[i]] = P(0,ss[i]);ans[ss[i]] = 0;}fo(i,1,sn){int x = ss[i];if (! stn) {st[++ stn] = x;vfa[x] = 0;continue;}int lca = Lca(x,st[stn]);for (;Dep[st[stn]] > Dep[lca];stn --)if (Dep[st[stn - 1]] <= Dep[lca]) vfa[st[stn]] = lca;if (st[stn] != lca){vs[++ vn] = lca;Dot[lca] = P(INF,0);vfa[lca] = st[stn];st[++ stn] = lca;}vfa[x] = lca;st[++ stn] = x;}sort(vs + 1,vs + 1 + vn,cmp_Dfsn);//注意到按dfs序排序是满足儿子一定在父亲的后面的 fo(i,1,vn){int x = vs[i];emp[x] = Son[x];if (i > 1) vfe[x] = Dep[x] - Dep[vfa[x]];}fd(i,vn,2){int x = vs[i],f = vfa[x];Dot[f] = min(P(Dot[x].first + vfe[x],Dot[x].second),Dot[f]);}fo(i,2,vn){int x = vs[i],f = vfa[x];Dot[x] = min(P(Dot[f].first + vfe[x],Dot[f].second),Dot[x]);}fo(i,1,vn){int x = vs[i],f = vfa[x];//树根往上的点 if (i == 1) {ans[Dot[x].second] += n - Son[x];continue;} int fp = Jump(x,Dep[f] + 1),cnt = Son[fp] - Son[x];emp[f] -= Son[fp]; //这一棵子树已经处理过了if (Dot[f].second == Dot[x].second){ans[Dot[x].second] += cnt;continue;}int mid = Dep[x] - (Dot[f].first + Dot[x].first + vfe[x]) / 2 + Dot[x].first;if ((Dot[f].first + Dot[x].first + vfe[x]) % 2 == 0 && Dot[f].second < Dot[x].second) mid ++;int tmp = Son[Jump(x,mid)] - Son[x];ans[Dot[x].second] += tmp;ans[Dot[f].second] += cnt - tmp;}fo(i,1,vn) ans[Dot[vs[i]].second] += emp[vs[i]];fo(i,1,sn){pint(ans[ss_[i]]);putchar(' ');}putchar(10);
}
int main()
{freopen("worldtree.in","r",stdin);freopen("worldtree.out","w",stdout);read(n); fo(i,1,n - 1){int x,y;read(x),read(y);Link(x,y);}Build_rt();read(Q);while (Q --){read(sn);fo(i,1,sn) read(ss[i]);Build_vt(); }return 0;
}
【HNOI2014】世界树(worldtree) (虚树详解)相关推荐
- BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】
题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...
- 虚树详解+例子分析+模板
概念: 给出一棵树. 每次询问选择一些点,求一些东西.这些东西的特点是,许多未选择的点可以通过某种方式剔除而不影响最终结果. 于是就有了建虚树这个技巧..... 我们可以用log级别的时间求出点对间的 ...
- 【学习笔记】线段树详解(全)
[学习笔记]线段树详解(全) 和三个同学一起搞了接近两个月的线段树,头都要炸了T_T,趁心态尚未凉之前赶快把东西记下来... [目录] [基础]作者:\((Silent\)_\(EAG)\) [懒标记 ...
- 机器学习算法(二十五):KD树详解及KD树最近邻算法
目录 1 KD树 1.1 什么是KD树 1.2 KD树的构建 1.3 KD树的插入 1.4 KD树的删除 1.5 KD树的最近邻搜索算法 1.5.1 举例:查询点(2.1,3.1) 1.5.2 举例: ...
- Trie树详解及其应用
原文:http://blog.csdn.net/hackbuteer1/article/details/7964147 Trie树详解及其应用
- B-、B树详解及模拟实现
B-.B树详解及模拟实现 文章目录 B-.B树详解及模拟实现 一.B-树 二.B树 1.性质 2.特性解释 3.B树的插入操作 4. B树的删除操作 5.B树摸拟实现 一.B-树 B-树就是B树 二. ...
- Android 驱动(12)---Linux DTS(Device Tree Source)设备树详解
Linux DTS(Device Tree Source)设备树详解 Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇) Linux DTS(Device Tr ...
- Jmeter之查看结果树详解
查看结果树详解 文章目录 查看结果树详解 前言 一.官网介绍 二.具体详解 1.左侧的结果列表 2.右侧的"取样器结果" 3.请求"选项卡 4."响应数据&qu ...
- 高通平台msm8953 Linux DTS(Device Tree Source)设备树详解之二(DTS设备树匹配过程)
本系列导航: 高通平台8953 Linux DTS(Device Tree Source)设备树详解之一(背景基础知识篇) 高通平台8953 Linux DTS(Device Tree Source ...
最新文章
- 生物信息3天速成班—成为团队中不可或缺的人
- 基于ONOS的T-SDN Super控制器
- try-with-resources语句
- 澳洲中本聪Craig Wright涉嫌论文抄袭?
- auot lisp 选择集处理_第64集 python机器学习:用预处理进行参数选择
- Android项目笔记整理(1)
- 面试再也不怕问到HashMap(一)
- CF1478A - Nezzar and Colorful Ball(数学)
- Spring Cloud config之一:分布式配置中心config server介绍
- python邮件发送 STMP
- html5毕业设计程序,网页毕业设计制作流程
- 工业相机——点阵相机与线阵相机
- 最近 火火火 的开源项目
- 姿态估计mmpose一手体验 Ⅱ - 使用它!
- 2015中国智能硬件蛋年创新大会手记
- 拼多多店铺昨天还好好的,今天突然没流量,怎么回事?
- C++中的堆内存、栈内存和静态内存
- 设置iPhone的呼叫转移来电等待
- 2020华师计算机基础客观作业,华师13春计算机基础作业
- 易班总是显示服务器出错,易班