洛谷P3233 世界树 [HNOI2014] 虚树
正解:虚树
解题报告:
传送门!
首先看到这种就要想到虚树这个是毫无疑问的QwQ
建虚树什么的都可以循规蹈矩地做,不说辣,具体可以看下虚树学习笔记什么的看下板子
但是建好虚树之后怎么搞还是有点儿讲究,所以专门开个题解港下(所以为什么好多人拿这个当做虚树入门题什么的昂,,,大概我太菜辣所以只有理解了半天趴QAQ(我我我我在网上找题解看到好多都是说,"首先求值很容易balabala",,,就很绝望,,,可能真的我太菜辣QAQ
首先读入dfs然后建虚树什么的跳过,直接到计算答案的地方
这里主要分为两大部分,分别港下w
(然后为了表述方便,先define一下,x的被管辖点y指y管x
首先找到虚树上每个节点的被管辖点(编号&距离
考虑到虚树上的点有两个情况,第一个是管辖点,显然属于自己,不说
然后如果不是管辖点,考虑到一定要么在子树中,要么不在子树中(...好废话啊)
如果在子树中,可以递归下去更新就好
如果不在子树中,考虑到管辖点和它的路径一定通过与父亲节点的连边,所以可以通过父亲节点的最近点更新
但是考虑到如果我们在做dfs的时候是先枚举儿子节点再计算这个点的结果的(实现算出管辖点在子树中的情况w
所以在做点x的时候实际上x的父亲的结果是还麻油求出来的
所以再另外dfs一遍就好了
然后关于这个还有另外一种理解方法,实现是一样的但是我还是大概港下QAQ
就是任意一条路径必然是向上走几步(也可以不走)再向下走几步
所以可以分别求出来向上向下,也就是我上面说的分别求出子树中和通过父亲节点w
(一个小技巧是由题意可得这个信息是以距离为第一关键字编号为第二关键字,所以用pair就很susi!
(虽然这个小技巧挺显然的我不说应该也能知道QAQ
upd:
打完代码之后发现有个之前麻油注意到的点,,,就是因为我们后面都是用的size昂这种的,就都是算的它子树里面的,然后如果有一个情况,给定的点都在最初dfs的根节点的一侧,那么另一侧的点就都统计不到辣(这个样例给得挺给力的,我就是测样例的时候发现的这个问题w
所以解决方法是在建虚树的时候直接强制性给它加入1号节点(就是最初dfs的根结点,因为我习惯直接从1号节点开始所以我是直接加1号
然后因为我是打完了代码才发现的这个问题,就不想对虚树进行大改了,所以就直接在原来的基础上判断一下有麻油1号,麻油直接强制加入就好,然后输出的时候注意特判一下不要输出多了/少了就好QwQ
il void dfs_fk_dtu(ll x)
{
if(ext[x])bl[x]=mp(,x);else bl[x]=mp(inf,);
e_fk(i,x){dfs_fk_dtu(t_fk(i));bl[x]=min(bl[x],mp(bl[t_fk(i)].fr+dep[t_fk(i)]-dep[x],bl[t_fk(i)].sc));}
}
il void dfs_fk_utd(ll x,ll dis,ll nm){if(bl[x]>mp(dis,nm))bl[x]=mp(dis,nm);e_fk(i,x)dfs_fk_utd(t_fk(i),bl[x].fr+dep[t_fk(i)]-dep[x],bl[x].sc);}这儿是代码w
然后计算每个管辖点管了多少个节点
现在考虑虚树上的点x,它的被管辖点是y,然后现在考虑它在实树上的儿子节点z,然后就花式分类讨论昂
可以想到如果z的子树中麻油管辖点,那么整棵z的被管辖点都是y,就可以ans+=size[z]
然后如果有管辖点,就比较难算,可以先当做整棵z的被管辖点都不是y,之后再把是y的加上,等下另说
那么实现就是直接在虚树上做,对虚树上的点x,首先被管辖点y的ans是+=size[x]的,然后枚举它在虚树上的儿子节点z,对所有儿子节点倍增跳到实树上的x节点的儿子节点(x-y那条链上的儿子节点昂
这时候考虑到我们把含有z的整棵子树都减辣,显然是不对的,反例都懒得举辣太显然辣,,,
所以就把减多了的加上来就好
所以就枚虚树上的边,对于边两侧的两点,如果他们的被管辖点是一样的,那在前面的计算中相当于减了又加了,不管
如果不一样,它们两个之间的点就都麻油被计入答案中
这里随便解释一下趴还是比较显然的,,,?
令这条边两侧的点是x和y,且x是y的父亲
那么y中的节点肯定在第一步中给y的被管辖点提供贡献辣(如果y中又有关键点什么的不要在意!当做解决辣就是!子问题而已!)
x子树中除了y的所有节点肯定在第一步中给x的被管辖点提供答案辣(x中有其他关键点也同上括号
综上,就只会有x到y之间的点麻油计入答案辣
所以就倍增跳啊跳地算出来就可以辣
最后说一下,,,看起来这儿又是要两次dfs的,其实显然一次就足够,所以我打的是一次dfs的w
il void dfs_cal(ll x)
{
ll siz=sz[x];
e_fk(i,x)
{
ll sn=chd(t_fk(i),x);dfs_cal(t_fk(i));
siz-=sz[sn];
if(bl[t_fk(i)]==bl[x])siz+=sz[sn]-sz[t_fk(i)];
else
{
ll tmp=t_fk(i);
my(j,,)
if(mp(dep[t_fk(i)]-dep[fa[tmp][j]]+bl[t_fk(i)].fr,bl[t_fk(i)].sc)<mp(dep[fa[tmp][j]]-dep[x]+bl[x].fr,bl[x].sc))tmp=fa[tmp][j];
as[bl[t_fk(i)].sc]+=sz[tmp]-sz[t_fk(i)];siz+=sz[sn]-sz[tmp];
}
bl[t_fk(i)]=mp(inf,);
}
as[bl[x].sc]+=siz;
head_fk[x]=;
}不建议直接复制粘贴,因为这个思想挺好的,还是自己打一下加深理解为好QAQ而且也麻油很多,自己好好理解然后打出来成就感还是max的呢!
综上,这道题就做完辣,最后只要把最初的构建虚树之类的代码和上面两个代码拼接起来就打完正解辣!
代码可能有点丑陋(压行+一堆宏定义+丑陋命名,我自己都有点嫌弃,,,我错了下次还敢TT
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define rg register
#define ll long long
#define fr first
#define sc second
#define gc getchar()
#define mp make_pair
#define t_rl(i) edge_rl[i].to
#define t_fk(i) edge_fk[i].to
#define w_rl(i) edge_rl[i].wei
#define w_fk(i) edge_fk[i].wei
#define rp(i,x,y) for(rg ll i=x;i<=y;++i)
#define my(i,x,y) for(rg ll i=x;i>=y;--i)
#define e_rl(i,x) for(rg ll i=head_rl[x];i;i=edge_rl[i].nxt)
#define e_fk(i,x) for(rg ll i=head_fk[x];i;i=edge_fk[i].nxt) const ll N=+,M=+,inf=1e12;
ll edge_rl_cnt,edge_fk_cnt,head_rl[N],head_fk[M],dep[N],fa[M][],n,m,ld_cnt,land[M],cnt_dfn,dfn[N],stck[N],head_stck,output[M],rt,as[N],sz[N];
bool ext[M];
pair<ll,ll>bl[N];
struct ed{ll to,nxt,wei;}edge_rl[N<<],edge_fk[M]; il ll read()
{
rg char ch=gc;rg ll x=;rg bool y=;
while(ch!='-' && (ch>'' || ch<''))ch=gc;
if(ch=='-')ch=gc,y=;
while(ch>='' && ch<='')x=(x<<)+(x<<)+(ch^''),ch=gc;
return y?x:-x;
}
il void ad_rl(rg ll x,rg ll y,rg ll z){edge_rl[++edge_rl_cnt]=(ed){y,head_rl[x],z};head_rl[x]=edge_rl_cnt;}
il void ad_fk(rg ll x,rg ll y,rg ll z){edge_fk[++edge_fk_cnt]=(ed){y,head_fk[x],z};head_fk[x]=edge_fk_cnt;}
void dfs_rl(ll nw,ll fat)
{
dfn[nw]=++cnt_dfn;
dep[nw]=dep[fat]+;sz[nw]=;fa[nw][]=fat;rp(i,,)fa[nw][i]=fa[fa[nw][i-]][i-];
e_rl(i,nw)if(t_rl(i)^fat)dfs_rl(t_rl(i),nw),sz[nw]+=sz[t_rl(i)];
}
il bool cmp(ll gd,ll gs){return dfn[gd]<dfn[gs];}
il ll lca(ll x,ll y)
{
if(dep[x]<dep[y])swap(x,y);
my(i,,)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
my(i,,)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][];
}
il ll chd(ll x,ll y){my(i,,)if(dep[fa[x][i]]>dep[y])x=fa[x][i];return x;}
il void build_fk()
{
head_stck=edge_fk_cnt=;
sort(land+,land++ld_cnt,cmp);
rp(i,,ld_cnt)
{
if(!head_stck)stck[++head_stck]=land[i];
else
{
ll grd=lca(stck[head_stck],land[i]);
if(grd==stck[head_stck]){stck[++head_stck]=land[i];continue;}
while(dep[stck[head_stck-]]>dep[grd])
ad_fk(stck[head_stck-],stck[head_stck],dep[stck[head_stck-]]-dep[stck[head_stck]]),--head_stck;
if(grd!=stck[head_stck-])ad_fk(grd,stck[head_stck],dep[grd]-dep[stck[head_stck]]),stck[head_stck]=grd,stck[++head_stck]=land[i];
else ad_fk(grd,stck[head_stck],dep[grd]-dep[stck[head_stck]]),stck[head_stck]=land[i];
}
}
while(head_stck>)ad_fk(stck[head_stck-],stck[head_stck],dep[stck[head_stck-]]-dep[stck[head_stck]]),--head_stck;rt=stck[head_stck];
}
il void dfs_fk_dtu(ll x)
{
if(ext[x])bl[x]=mp(,x);else bl[x]=mp(inf,);
e_fk(i,x){dfs_fk_dtu(t_fk(i));bl[x]=min(bl[x],mp(bl[t_fk(i)].fr+dep[t_fk(i)]-dep[x],bl[t_fk(i)].sc));}
}
il void dfs_fk_utd(ll x,ll dis,ll nm){if(bl[x]>mp(dis,nm))bl[x]=mp(dis,nm);e_fk(i,x)dfs_fk_utd(t_fk(i),bl[x].fr+dep[t_fk(i)]-dep[x],bl[x].sc);}
il void dfs_cal(ll x)
{
ll siz=sz[x];
e_fk(i,x)
{
ll sn=chd(t_fk(i),x);dfs_cal(t_fk(i));
siz-=sz[sn];
if(bl[t_fk(i)]==bl[x])siz+=sz[sn]-sz[t_fk(i)];
else
{
ll tmp=t_fk(i);
my(j,,)
if(mp(dep[t_fk(i)]-dep[fa[tmp][j]]+bl[t_fk(i)].fr,bl[t_fk(i)].sc)<mp(dep[fa[tmp][j]]-dep[x]+bl[x].fr,bl[x].sc))tmp=fa[tmp][j];
as[bl[t_fk(i)].sc]+=sz[tmp]-sz[t_fk(i)];siz+=sz[sn]-sz[tmp];
}
bl[t_fk(i)]=mp(inf,);
}
as[bl[x].sc]+=siz;
head_fk[x]=;
}
il void cal()
{
dfs_fk_dtu(rt);dfs_fk_utd(rt,bl[rt].fr,bl[rt].sc);
/*以上都是正确dei!然而就这个cal出错辣1551*/
if(!ext[])--ld_cnt;
dfs_cal(rt);rp(i,,ld_cnt)printf("%lld ",as[output[i]]),as[output[i]]=,ext[output[i]]=;printf("\n");
} int main()
{
freopen("sjs.in","r",stdin);freopen("sjs.out","w",stdout);
n=read();rp(i,,n-){ll x=read(),y=read();ad_rl(x,y,);ad_rl(y,x,);}dfs_rl(,);
m=read();rp(i,,m){ld_cnt=read();rp(i,,ld_cnt)ext[output[i]=land[i]=read()]=;if(!ext[])land[++ld_cnt]=;build_fk();;cal();}
return ;
}
/*
,,,感jio自己命名有点儿玄学,,,在后边解释下好了quq
其实也麻油几个要解释的呢QAQ?
一个是rl-real,表示最开始的实树
一个是fk-fake,表示后来建起来的虚树
一个是utd-uptodown,表示从上往下更新
一个是dtu-downtoup,表示从下往上更新
好像也麻油辣?
*/
/*清零操作不要忘了,,,调死我了,,,wsl*/
这儿是代码,,,打死我了TT(然后灵巧的蛇皮命名方式写在后面辣!
洛谷P3233 世界树 [HNOI2014] 虚树的更多相关文章
- 洛谷P3233 世界树
题意:给定树上k个关键点,每个点属于离他最近,然后编号最小的关键点.求每个关键点管辖多少点. 解:虚树 + DP. 虚树不解释.主要是DP.用二元组存虚树上每个点的归属和距离.这一部分是二次扫描与换根 ...
- bzoj 2286(洛谷 2495) [Sdoi2011]消耗战——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2286 https://www.luogu.org/problemnew/show/P2495 ...
- ●洛谷P3233 [HNOI2014]世界树
题链: https://www.luogu.org/problemnew/show/P3233题解: 虚树,dp,倍增. 首先对于每个询问,要把虚树建出来,这一步就从略了.这里着重分享一下如何求答案. ...
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- 洛谷p3384【模板】树链剖分题解
洛谷p3384 [模板]树链剖分错误记录 首先感谢\(lfd\)在课上调了出来\(Orz\) \(1\).以后少写全局变量 \(2\).线段树递归的时候最好把左右区间一起传 \(3\).写\(dfs\ ...
- 洛谷 P3233 [HNOI2014]世界树(虚树+dp)
题面 luogu 题解 数据范围已经告诉我们是虚树了,考虑如何在虚树上面\(dp\) 以下摘自hzwer博客: 构建虚树以后两遍dp处理出虚树上每个点最近的议事处 然后枚举虚树上每一条边,考虑其对两端 ...
- 洛谷P3233 [HNOI2014]世界树
虚树= = #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring&g ...
- bzoj 3572 [Hnoi2014]世界树(虚树+DP)
3572: [Hnoi2014]世界树 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 645 Solved: 362[Submit][Status] ...
- BZOJ3572 [Hnoi2014]世界树 【虚树 + 树形dp】
题目 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条里,公平是使世界树能够生生不息.持续运转的根本基石. ...
随机推荐
- Java知多少(34)final关键字:阻止继承和多态
在 Java 中,声明类.变量和方法时,可使用关键字 final 来修饰.final 所修饰的数据具有“终态”的特征,表示“最终的”意思.具体规定如下: final 修饰的类不能被继承. final ...
- C#基础回顾:正则表达式-转
写在前面:本文根据笔者的学习体会结合相关书籍资料对正则表达式的语法和使用(C#)进行基本的介绍.适用于初学者. 摘要:正则表达式(Regular Expressions),相信做软件开发的朋友或多或少 ...
- [Bayes] Understanding Bayes: Visualization of the Bayes Factor
From: https://alexanderetz.com/2015/08/09/understanding-bayes-visualization-of-bf/ Nearly被贝叶斯因子搞死,找篇 ...
- [UI] 04 - Bootstrap: layout & navigation
前言 一.资源 From: http://www.imooc.com/code/2881 Ref: http://www.runoob.com/try/bootstrap/layoutit/[拖拽布局 ...
- [React] 07 - Flux: uni-flow for react
相关资源 Ref: [Android Module] 03 - Software Design and Architecture Ref: Flux 架构入门教程 Ref: 详解React Flux架 ...
- 一个整型数组里除了一个数字之外,其他的数字都出现了两次。要求时间复杂度是O(n),空间复杂度是O(1),如何找出数组中只出现一次的数字
思路分析:任何一个数字异或它自己都等于0,根据这一特性,如果从头到尾依次异或数组中的每一个数字,因为那些出现两次的数字全部在异或中抵消掉了,所以最终的结果刚好是那些只出现一次的数字. 代码如下: #i ...
- 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V2 -- 本地方法调用 整合 Spring
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——RPC -- 本地方法调用 + Spring 1. 配置applicationContext.xml文件 注入 bean 及 管理 bean ...
- 8. Oracle通过rman进行克隆
一. 安装规划 IP ROLE 192.168.1.235 克隆对象 192.168.1.221 克隆库 二. 备库创建相应的dump文件夹 # 在主库查询对应的dump目录 select name, ...
- Mariadb 10.3.5 序列号(sequence) 尝鲜
除了Oracle Mariadb 也有sequence了,前提是Mariadb 10.3以上版本才支持. 但目前的正式版依然是10.2 启动一个Mariadb 10.3.5 docker pull m ...
- Python数据结构———栈
线性数据结构 当添加一个项目时,它就被放在这样一个位置:在之前存在的项与后来要加入的项之间.像这样的数据集合常被称为线性数据结构. 栈 栈是一个项的有序集合.添加项和移除项都发生在同一“端”,这一端通 ...