世界树<题解>

首先我们拿到这个题之后,能想到的一定是虚树,如果想不到的话,还是重新学一遍去吧

所以我们应该怎么做呢

虚树的板子不需要我再讲一遍了吧

所以对于这个题来说,怎么根据虚树上的节点来找到每一个点的集合的大小

针对这种在树上求集合大小的题,不是dp就是利用siz(子树大小)来容斥求得

然而这个题就是巧妙的利用了siz这个东西

(注意接下来说的,儿子是指虚树中的儿子,子树是指原树中的子树)

vis[]数组用来标记这个节点是否为议事处

我们利用who[]和dis[]两个数组来实现在这个虚树上的容斥

who[x]表示距离x这个节点最近的被标记的节点

dis[x]表示x与who[x]之间的距离

我们想要求这两个值,就需要分为两部分去求,父亲那边的,虚树子树内的

所以我们进行两次dfs,一次向下寻找,一次向上寻找,得到这个数组

int who[N],dis[N];
void dfs_sol1(int x){
who[x]=dis[x]=inf;
if(vis[x])who[x]=x,dis[x]=0;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
dfs_sol1(y);
int tmp=DIS(x,who[y]);
if(tmp<dis[x]||tmp==dis[x]&&who[y]<who[x])who[x]=who[y],dis[x]=tmp;
}
}
void dfs_sol2(int x){
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
int tmp=DIS(y,who[x]);
if(tmp<dis[y]||tmp==dis[y]&&who[x]<who[y])who[y]=who[x],dis[y]=tmp;
dfs_sol2(y);
}
}

为什么不需要分为两种,父亲的和儿子的

因为我们对于每一个节点,他和他儿子的who不一样的话

要么他儿子本身就被标记过了,要么是儿子的who在儿子的虚树子树内

所以不会出现有两组x到who[x]的路径重复的情况

所以我们只需要记录一个最近节点

那么最后我们就需要去求每个议事处的集合大小了

既然是在树上,我们很容易想到是要用到一个dfs的

那我们每遍历到一个节点就将这个节点的siz加到这个节点的who上

然后我们就进行一次判断,

     如果此时节点与他的儿子节点的who相同,那我们就直接减去儿子节点的siz

     如果不相同的话,我们就利用倍增的办法,找到这个分界点,使得分界点两侧一侧是分到
当前节点的who上,一侧是分到儿子节点的who上,这样我们直接利用siz进行加减就好了

如何找到分界点呢?

因为每两个点之间的距离都是1,所以这个分界点一定是这条链的中点,再特判一下,搞定中点就可以啦

为什么要用倍增??因为快

$ code $



#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
#define inf 0x3f3f3f3f
const int N=300005;
int n,q,m,h[N];
int to[N*2],nxt[N*2],head[N],rp;
int dfn[N],cnt;
int dep[N],fa[N][21],siz[N];
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
void dfs_first(int x){
dfn[x]=++cnt;
siz[x]=1;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
if(y==fa[x][0])continue;
fa[y][0]=x;
for(re i=1;i<=20;i++)fa[y][i]=fa[fa[y][i-1]][i-1];//cout<<fa[y][i]<<endl;
dep[y]=dep[x]+1;
dfs_first(y);
siz[x]+=siz[y];
}
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(re i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
if(x==y)return x;
for(re i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int DIS(int x,int y){
return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
int sta[N],tot;
bool vis[N];
bool cmp(int x,int y){
return dfn[x]<dfn[y];
}
void build_vtree(){
sort(h+1,h+m+1,cmp);
sta[tot=1]=1;head[1]=0;rp=0;
for(re i=1;i<=m;i++){
if(h[i]==1)continue;
int lca=LCA(sta[tot],h[i]);
//cout<<lca<<endl;
if(lca!=sta[tot]){
while(dfn[lca]<dfn[sta[tot-1]])
add_edg(sta[tot-1],sta[tot]),tot--;
if(lca!=sta[tot-1]){
head[lca]=0;
add_edg(lca,sta[tot]);
sta[tot]=lca;
}
else add_edg(lca,sta[tot]),tot--;
}
head[h[i]]=0;
sta[++tot]=h[i];
}
//cout<<tot<<endl;
for(re i=1;i<tot;i++)add_edg(sta[i],sta[i+1]);
}
int who[N],dis[N];
void dfs_sol1(int x){
who[x]=dis[x]=inf;
if(vis[x])who[x]=x,dis[x]=0;
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
dfs_sol1(y);
int tmp=DIS(x,who[y]);
if(tmp<dis[x]||tmp==dis[x]&&who[y]<who[x])who[x]=who[y],dis[x]=tmp;
}
}
void dfs_sol2(int x){
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
int tmp=DIS(y,who[x]);
if(tmp<dis[y]||tmp==dis[y]&&who[x]<who[y])who[y]=who[x],dis[y]=tmp;
dfs_sol2(y);
}
}
int get_fa(int x,int t){
for(re i=20;i>=0;i--)
if(t>=1<<i)
x=fa[x][i],t-=1<<i;
return x;
}
int ans[N];
void dfs_ans(int x){
ans[who[x]]+=siz[x];
for(re i=head[x];i;i=nxt[i]){
int y=to[i];
//cout<<x<<" "<<who[x]<<" "<<y<<" "<<who[y]<<endl;
if(who[x]==who[y])ans[who[x]]-=siz[y];
else{
int tmp=DIS(x,y)-1+dis[x]-dis[y]>>1;
if(!(DIS(who[x],who[y])&1)&&who[y]<who[x])tmp++;
int z=get_fa(y,tmp);
ans[who[x]]-=siz[z];
ans[who[y]]+=siz[z]-siz[y];
}
dfs_ans(y);
}
}
signed main(){
scanf("%d",&n);
for(re i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add_edg(x,y);
add_edg(y,x);
}
dep[1]=1;
dfs_first(1);
scanf("%d",&q);
int an[N];
while(q--){
scanf("%d",&m);
for(re i=1;i<=m;i++){
scanf("%d",&h[i]);
an[i]=h[i];
vis[h[i]]=1;
}
build_vtree();
dfs_sol1(1);
dfs_sol2(1);
for(re i=1;i<=m;i++)ans[an[i]]=0;
dfs_ans(1);
for(re i=1;i<=m;i++)printf("%d ",ans[an[i]]);
printf("\n");
for(re i=1;i<=m;i++)vis[h[i]]=0;
}
}

完事了。。。。。

<题解>世界树的更多相关文章

  1. BZOJ3572:[HNOI2014]世界树——题解

    +++++++++++++++++++++++++++++++++++++++++++ +本文作者:luyouqi233. + +欢迎访问我的博客:http://www.cnblogs.com/luy ...

  2. [题解] [HNOI2014] 世界树

    题面 [HNOI2014]世界树 题解 从数据范围很容易看出是个虚树DP(可惜看出来了也还是不会做) 虚树大家应该都会, 不会的话自己去搜吧, 我懒得讲了, 我们在这里只需要考虑如何DP即可 首先我们 ...

  3. [题解](树形dp/换根)小x游世界树

    2. 小x游世界树 (yggdrasi.pas/c/cpp) [问题描述] 小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当 ...

  4. 【题解】HNOI2014世界树

    脑子不清醒的时候千万别写题.写题写不下去了千万别死扛,重构才是你唯一的出路QAQ 昨天很想快点写道题,思路没有很清晰的时候就写了,结果……今天一怒之下决定重整思路重构代码,其实不过是半个小时的事情…… ...

  5. 题解 P3233 [HNOI2014]世界树

    题目传送门 解题思路 正解当然是虚树了. 首先对于原树以及虚树各开一个结构体存边,这个不用多说. 然后我们先 DFS 一遍,求出各个节点的时间戳,子树大小,深度以及父亲节点,并初始化倍增 LCA . ...

  6. [BZOJ3572][Hnoi2014]世界树

    [BZOJ3572][Hnoi2014]世界树 试题描述 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森,在他们的信条 ...

  7. # HNOI2012 ~ HNOI2018 题解

    HNOI2012 题解 [HNOI2012]永无乡 Tag:线段树合并.启发式合并 联通块合并问题. 属于\(easy\)题,直接线段树合并 或 启发式合并即可. [HNOI2012]排队 Tag:组 ...

  8. 【BZOJ3572】[Hnoi2014]世界树 虚树

    [BZOJ3572][Hnoi2014]世界树 Description 世界树是一棵无比巨大的树,它伸出的枝干构成了整个世界.在这里,生存着各种各样的种族和生灵,他们共同信奉着绝对公正公平的女神艾莉森 ...

  9. bzoj 3572: [Hnoi2014]世界树 虚树

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

随机推荐

  1. JAVA基础——运算符号

    运算符(java) 算数运算符:+,-,*,/,%(取余),++,-- 赋值运算符:= 关系运算符:<, >, >= ,<= ,== , != 逻辑运算符:&& ...

  2. [物联网] 电气 & 工控

    原理 一次回路和二次回路 一次回路:强电部分(380伏---22万伏),连接发电机.电动机.变压器.电网线路.电网开关.电网避雷器等等 二次回路:弱电部分,指的是控制线路.保护线路.测量线路.计量线路 ...

  3. [Scala] 面向对象

    类定义 当属性是private时,scala会自动为其生成get和set方法 只希望scala生成get,不生成set,可定义为常量 不生成get和set方法,使用private[this]关键字 1 ...

  4. R语言执行脚本的几种命令

    R CMD BATCH 和 Rscript 使用前都要先添加环境变量 把 C:\Program Files\R\R-3.3.0\bin; 加到"系统变量"的Path 值的最开始 可 ...

  5. Linux_计划任务理论概述

    一.计划任务概述 1.计划任务概述: 计划任务分为: 一次性任务 周期性任务 在Linux系统的计划任务服务crond 可以满足周期性执行任务的需求. crond进程每分钟会处璇一次计划任务,计划任务 ...

  6. nginx负载均衡搭建phpmyadmin加入redis了解session会话原理

    myphpadmin项目理解cookie和session 当我们平时上网的时候,在刷新之后或者退出浏览器再次打开浏览器不需要登陆网页了,这就是利用了cookie和session: 环境配置 hostn ...

  7. xpath定位中starts-with、contains、text()的用法

    starts-with 顾名思义,匹配一个属性开始位置的关键字 contains 匹配一个属性值中包含的字符串 text() 匹配的是显示文本信息,此处也可以用来做定位用 eg //input[sta ...

  8. python3 smtplib发送邮件

    使用smtp包发送邮件还依赖email的一些方法 发送邮件主要分为三步: 1,定义邮箱参数:邮箱服务器地址,邮箱用户名,邮箱密码,邮件发送方,邮件接收方,邮件标题,邮件内容 2,配置发送内容 3,实例 ...

  9. Ubuntu 16.04搭建php5.6 Web服务器环境

    Ubuntu 16.04默认安装php7.0环境,但是php7目前兼容性并不是很好,如果自行安装php5需要清除php7的已安装包,否则会报错. 移除默认及已安装的PHP包 sudo dpkg -l ...

  10. 微服务架构(Microservices) ——Martin Flower

    不知不觉到达了Sring Boot的学习中了,在学习之前,了解微服务架构是很有必要的,对于自己提升今后面试的软实力有很大帮助,在此写下. 让我们接下来看下Martin Flower 如何解释微服务架构 ...