「HNOI2014」世界树

前置技能:虚树。

(本题可以通过以下相似的思想用线段树维护子树信息和倍增找中点完成,代码短很多,但本篇题解不涉及)

题解部分

这种总询问点数不大,但是询问次数多,可以想到用虚树来优化。

我们把所有询问点看成关键点建一颗虚树。对于虚树上的点我们是可以求出离它最近的点。那么对于那些被隐藏的点。这里有两种被隐藏的点。(以下为有根树)

其中黑色和橙色的为虚树上的点,灰色的就是被隐藏的点。对于这些被隐藏的点,离他们最近的临时议事处就是离橙色的点最近的临时议事处。这个可以通过维护一个sz数组,sz[i]表示i在原树中的子树的节点个数。那么答案就是sz[1]减去所有P类子节点的sz(P类子节点就是这个子节点的子树在虚树中有出现,P类这个名字是乱取的)

已知点1,5为虚树上的点,其他点为虚树上的点,点1的深度较低。我们如果知道了不在2的子树内的离1最近的临时议事处离1的距离及其编号,也知道5的子树内的离5距离最近临时议事处的的距离及其编号。对于1,到5这条链上的点,那么我们就可以求出一个深度临界值,在这个深度及其以上的都被离1最近的临时议事处所管辖,在这个深度以下的都被离5最近的临时议事处所管辖。由于像8,9的这些点,他们归属的临时议事处和他们所对应的链上的点(如8,9对应3)是一样的,故和他们一起考虑。

那么我们要预处理出每个点子树内离它最近与次近的临时议事处,每个点子树内除它和除它子树内节点离他最近的临时议事处。用每个点子树内离它次近的临时议事处与每个点除子树内节点离他最近的临时议事处就可求出每个点除任意一个子节点的子树外离它最近的临时议事处。

记离5最近的临时议事处编号为X,离1最近的临时议事处编号为Y。

假设3,4归X管辖,2归Y管辖。那么X管辖这一部分的点数为sz[3]-sz[5],Y管辖这一部分的点数为sz[2]-sz[3]。(这里可以用倍增的方式维护父亲,就可以找到1,5这条链上1的子节点)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define M 300005
using namespace std;
struct E{int to,nx;
}edge[M<<1];
int tot,head[M];
void Addedge(int a,int b){edge[++tot].to=b;edge[tot].nx=head[a];head[a]=tot;
}
int fa[M][20],dep[M],sz[M];
int Dfn[M],tot_id;
void dfs_Init(int now){sz[now]=1;Dfn[now]=++tot_id;//预处理DFS序 for(int i=1;i<20;i++)fa[now][i]=fa[fa[now][i-1]][i-1];//倍增的fa数组 for(int i=head[now];i;i=edge[i].nx){int nxt=edge[i].to;if(nxt==fa[now][0])continue;fa[nxt][0]=now;dep[nxt]=dep[now]+1;dfs_Init(nxt);sz[now]+=sz[nxt];}
}
int Up(int x,int y){//求x这个点的第y个父亲 for(int i=0;i<20;i++)if((1<<i)&y)x=fa[x][i];return x;
}
int LCA(int x,int y){//倍增求LCA if(dep[x]<dep[y])swap(x,y);x=Up(x,dep[x]-dep[y]);if(x==y)return x;for(int i=19;i>=0;i--){if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];}return fa[x][0];
}
bool cmp_dfn(int x,int y){return Dfn[x]<Dfn[y];
}
int stk[M];
int tmp[M];
void i_Build(int Q[],int len){//建虚树 for(int i=1;i<=len;i++)tmp[i]=Q[i];sort(tmp+1,tmp+len+1,cmp_dfn);//按dfs序排序 int top=0;if(tmp[1]!=1)stk[++top]=1;for(int i=1;i<=len;i++){if(top<=1){stk[++top]=tmp[i];continue;}int lca=LCA(tmp[i],stk[top]);if(lca==stk[top]){stk[++top]=tmp[i];continue;}while(top>1&&Dfn[lca]<=Dfn[stk[top-1]]){Addedge(stk[top-1],stk[top]);top--;}if(stk[top]!=lca){Addedge(lca,stk[top]);stk[top]=lca;}stk[++top]=tmp[i];}while(top>1){Addedge(stk[top-1],stk[top]);top--;}
}
bool mark[M];
int Q[M];
int Near[M][2];//Near[i][0]在这个点下面的离它最近的 1在这个点上面
int dis[M][2];
int se_near[M],se_dis[M];
bool check(int x,int y,int a){//y这个点是否比x离a更近(这里的x,y分别于a有祖先关系) if(x==0)return 1;if(y==0)return 0;int dis1=abs(dep[x]-dep[a]),dis2=abs(dep[y]-dep[a]);if(dis1==dis2)return y<x;return dis2<dis1;
}
bool check(int x,int dis1,int y,int dis2){//y这个点是否比x离a更近if(x==0)return 1;if(y==0)return 0;if(dis1==dis2)return y<x;return dis2<dis1;
}
void dfs_low(int now){//预处理出一个点的子树内离他最近和次近的临时议事处 int &res=Near[now][0];int &res0=se_near[now];dis[now][0]=1e9;se_dis[now]=1e9;res=res0=0;if(mark[now])res=now;for(int i=head[now];i;i=edge[i].nx){int nxt=edge[i].to;dfs_low(nxt);if(check(res,Near[nxt][0],now))res0=res,res=Near[nxt][0];else if(check(res0,Near[nxt][0],now))res0=Near[nxt][0];}if(res)dis[now][0]=dep[res]-dep[now];if(res0)se_dis[now]=dep[res0]-dep[now];
}
void dfs_upp(int now,int pre,int pre_dis){//预处理一个点除它和它子树的点离他最近的临时议事处 int &res=Near[now][1];dis[now][1]=1e9;res=0;if(mark[now]){res=now;dis[now][1]=0;pre=now;pre_dis=0;}else res=pre,dis[now][1]=pre_dis;for(int i=head[now];i;i=edge[i].nx){int nxt=edge[i].to;int nxt_near=pre,nxt_dis=pre_dis;if(Near[nxt][0]==Near[now][0]){//求除nxt的子树外其他now子树的点离now的最近的临时议事处 if(check(nxt_near,nxt_dis,se_near[now],se_dis[now]))nxt_near=se_near[now],nxt_dis=se_dis[now];}else if(check(nxt_near,nxt_dis,Near[now][0],dis[now][0]))nxt_near=Near[now][0],nxt_dis=dis[now][0];dfs_upp(nxt,nxt_near,nxt_dis+dep[nxt]-dep[now]);//注意这里的两点间的距离不是1而是 dep[nxt]-dep[now] }
}
int Ans[M];
void dfs_ans(int now){int mx_Near;if(check(Near[now][1],dis[now][1],Near[now][0],dis[now][0]))mx_Near=Near[now][0];else mx_Near=Near[now][1];int add=sz[now];for(int i=head[now];i;i=edge[i].nx){int nxt=edge[i].to;//决策now到nxt这条边上(对应原树)的点(不包括两端)int cnt=dep[nxt]-dep[now]-1,top,now_son,val,dis1,dis2,del_dis;//cnt为这条链上不包括两端的点的数量,now_son表示now到nxt的链上now的子节点。//dis1表示nxt子树内到nxt的最近的临时议事处到nxt的距离(这个点的编号记为X) //dis2表示除now_son及其子树内的点到now的最近的临时议事处到now的距离 (这个点的标号记为Y) //del_dis表示dis2-dis1 //val表示Y可以控制多少个now到nxt的链(原树上)上(不包括端点)的点//top Y能控制深度最浅的now到nxt的链上的点的编号 int up_Near=Near[now][1],up_dis=dis[now][1];//求出除nxt所对应now的子节点的子树的点外离now最近的临时议事处 if(Near[now][0]==Near[nxt][0]){if(check(up_Near,up_dis,se_near[now],se_dis[now]))up_Near=se_near[now],up_dis=se_dis[now];}else if(check(up_Near,up_dis,Near[now][0],dis[now][0]))up_Near=Near[now][0],up_dis=dis[now][0];if(cnt>0){dis1=dis[nxt][0],dis2=up_dis,del_dis=dis2-dis1;if(abs(del_dis)>=cnt){if(del_dis>=0)val=cnt;else val=0;}else {//减去del_dis后平均分配,若为奇数则编号小的那个多1 if(del_dis>0)val=del_dis+floor(1.0*(cnt-del_dis)/2.0);else if(del_dis<=0)val=floor(1.0*(cnt+del_dis)/2.0);if((cnt-abs(del_dis))%2==1&&Near[nxt][0]<up_Near)val++;}val=max(val,0); top=Up(nxt,val),now_son=Up(nxt,dep[nxt]-dep[now]-1);Ans[Near[nxt][0]]+=sz[top]-sz[nxt];//计算答案 Ans[up_Near]+=sz[now_son]-sz[top];}else now_son=Up(nxt,dep[nxt]-dep[now]-1);add-=sz[now_son];dfs_ans(nxt);}Ans[mx_Near]+=add;//算题解中所说的情况1的答案
}
void dfs_clear(int now){for(int i=head[now];i;i=edge[i].nx){//清空 dfs_clear(edge[i].to);}head[now]=0;
}
int main(){int n,m;scanf("%d",&n);for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);Addedge(a,b);Addedge(b,a);}dfs_Init(1);memset(head,0,sizeof(head));tot=0;scanf("%d",&m);while(m--){int q;scanf("%d",&q);for(int i=1;i<=q;i++)scanf("%d",&Q[i]),mark[Q[i]]=1,Ans[Q[i]]=0;i_Build(Q,q);dfs_low(1);dfs_upp(1,0,1e9);dfs_ans(1);for(int i=1;i<=q;i++)printf("%d ",Ans[Q[i]]),mark[Q[i]]=0;puts("");dfs_clear(1);tot=0;}return 0;
}

「HNOI2014」世界树 虚树相关推荐

  1. [HNOI2014]世界树 (虚树DP+倍增)

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

  2. bzoj 3572 [Hnoi2014]世界树——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3572 关于虚树:https://www.cnblogs.com/zzqsblog/p/556 ...

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

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

  4. bzoj3572世界树 虚树+树型动规

    3572: [Hnoi2014]世界树 Time Limit: 20 Sec   Memory Limit: 512 MB Submit: 1786   Solved: 957 [ Submit][ ...

  5. luogu3233 世界树 (虚树)

    反正肯定要建虚树,考虑建完之后怎么做 先随便dp一下算出来距离某点最近的询问点mi[x](因为有的虚树上的点它不是询问点嘛) 那我们对于某条链x到fa[x]上的非虚树上的点(包括他们的非虚树上的孩子) ...

  6. 等比数列三角形 (数论 + 黄金分割点)+ JOISC 2016 Day3 T3 「电报」(基环树 + 拓扑排序)

    文章目录 T1:等比数列三角形 题目 题解 代码实现 T2:电报 题目 题解 代码实现 T1:等比数列三角形 题目 求三边都是 ≤n 的整数,且成等比数列的三角形个数 注意三角形面积不能为 0 注意 ...

  7. LOJ 2312(洛谷 3733) 「HAOI2017」八纵八横——线段树分治+线性基+bitset

    题目:https://loj.ac/problem/2312 https://www.luogu.org/problemnew/show/P3733 原本以为要线段树分治+LCT,查了查发现环上的值直 ...

  8. 「题解」:[线段树]:永无乡

    题面 题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 ...

  9. 「机器学习」机器学习算法优缺点对比(汇总篇)

    作者 | 杜博亚 来源 | 阿泽的学习笔记 「本文的目的,是务实.简洁地盘点一番当前机器学习算法」.文中内容结合了个人在查阅资料过程中收集到的前人总结,同时添加了部分自身总结,在这里,依据实际使用中的 ...

最新文章

  1. 数据库设计和管理规范
  2. 摆脱“人肉”审核,从0搭建可视化SQL自动审核平台
  3. 神策数据加入猿团程序员大牛卡,创客大礼包助力开发。
  4. 投稿Cover Letter如何写出彩
  5. CUBA 7的新功能
  6. LeetCode 101. 对称二叉树(递归循环)
  7. android 获取程序名,Android_Android获取应用程序名称(ApplicationName)示例,MainActivity如下: 复制代码 代码 - phpStudy...
  8. linux amd显卡调风扇转速,显卡风扇转速设置教程方法
  9. matlab调和均值滤波_均值滤波和中值滤波的MATLAB实现
  10. FPS通用的方框透视公式的原理
  11. eclipse php jquery,Eclipse 支持jQuery 自动提示
  12. 服务器和交换机物理连接_什么是光纤交换机?有什么功能?
  13. 计算机提示策略阻止安装,win7安装软件提示此程序被组策略阻止怎么办
  14. 共享计算机ip地址,怎么设置局域网计算机IP地址:局域网计算机共享设置
  15. pygame UI 框架
  16. Gartner 2016数据仓库和数据分析数据库管理解决方案魔力象限
  17. python中bind的用法_Python socket.bind方法代码示例
  18. ubuntu | 命令行中输出文件夹下的文件+输出某个后缀的文件+文件名作为参数运行py脚本
  19. python 的1970年秒数和datetime互相转换
  20. 全球工业互联网平台应用案例分析报告

热门文章

  1. wiki程序php,开源WIKI引擎程序Dokuwiki
  2. 由“你”而生的公司危机【网络新生媒体的力量】
  3. js判断浏览器内核(ie11特殊判断)
  4. 【纪中集训2019.3.12】Mas的仙人掌
  5. Kindeditor上传本地图片成功后不回显,弹出层也不关闭,解决办法
  6. js 实现rgb和十六进制的代码转化
  7. STM32使用EMWin实现中文字体显示
  8. 【组件封装】vue打字机效果和文字发光
  9. VS 中使用xcopy命令 发生 :VCEnd exited with code 4 错误
  10. 计算机内存和寄存器,寄存器和内存的区别