洛谷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中的数据加密3 非对称加密
非对称加密也加公钥加密,不对称算法使用一对密钥对,一个公钥,一个私钥,使用公钥加密的数据,只有私钥能解开(可用于加密):同时,使用私钥加密的数据,只有公钥能解开(签名).但是速度很慢(比私钥加密慢10 ...
- idea配置项目运行时内存大小
选择 edit Configurations : -server -XX:PermSize=1024M -XX:MaxPermSize=2048M
- 对TextFile格式文件的lzo压缩建立index索引
转自:http://blog.csdn.net/yangbutao/article/details/8519572 hadoop中可以对文件进行压缩,可以采用gzip.lzo.snappy等压缩算法. ...
- 实验室ubuntu连ipv6
1.买个极路由 2.无线中继连tsinghua-5G 3.安装ipv6插件 4.联网或者科协vpn 5.下载bt客户端:sudo apt-get install qbittorrent (或者su ...
- 关于SpringBoot如何返回视图
别人已经写过了,我就不重复造轮子了.我赞成他的方案:Spring Boot使用方法小札(1):Web应用返回jsp页面 如果配置完之后,访问相应的Controller 还是得不到对应的页面,考虑用以下 ...
- Android 数据库 大量插入 事务开启
对比在Android中批量插入数据的3中方式对比(各插入1W条数据所花费的时间): 1. 一个一个插入 publicstaticboolean insert(SQLiteOpenHelper open ...
- [Bayes] runif: Inversion Sampling
runifum Inversion Sampling 看样子就是个路人甲. Ref: [Bayes] Hist & line: Reject Sampling and Importance S ...
- SQL Server -- 随笔
-- 判断是否存在 LimeNextMonthBirthday 表 ) PRINT '存在' ELSE PRINT'不存在' -- 如果存在 则删除 没有 则打印 不存在 ) DROP TABLE L ...
- Servlet知识点回顾
一.Servlet生命周期 服务器调用一个Servlet的8个步骤: 1.在服务器启动时,当Servlet被配置好或者被客户首次请求时,由服务器加载servlet,这一步相当于下列代码: Class ...
- scala 模式匹配详解 2 scala里是怎么实现的?
在这篇martin和另外两位模式匹配领域专家的论文里说了模式匹配的几种实现方式,以及scala是选择哪种方式来实现的.http://lampwww.epfl.ch/~emir/written/Matc ...