题目链接:点击查看

题目大意:给出n棵树,再给出m个询问,每次询问给出两个整数u和k,先假设u在k层之上的祖先是p,问与u在同一层深度,并且公共祖先都是p的节点有多少个

题目分析:因为先要求出u在第k层之上的祖先,我们可以利用树上倍增很简单的求出,但接下来的操作我是没想到的,虽然鲲学长已经给了提示需要二分,我也看出了直接dfs暴力找肯定会T掉的,但没设计出合适的二分来处理这个题,憋不住去网上看了题解后一下子就豁然开朗了,大概就是先用dfs序预处理一下,然后每一层开一个vector,用来储存每个节点在dfs序后的编号,因为dfs序的缘故,我们在求出u在第k层之上的祖先p后,就可以知道p的子树中的所有编号肯定都在[L[p],R[p]]之间了,那么我们只需要对于deep[u]这一层的向量进行二分,求出有多少个节点位于这个范围内就是答案了

实现简单但思路不好想,挺好的一个题目


2020. 1.14更新:

学了一波树上启发式合并,发现对于这个题目而言,要求以每个结点为根的子树的状态,不妨直接用启发式合并搞一波,因为给出的数据是针对于子节点和相对深度,我们可以利用树上倍增预处理出祖先节点和绝对深度,如此一来就是树上启发式合并的模板题了

代码:

二分:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;vector<int>node[N];vector<int>d[N],root;int n,limit,max_deep;int cnt=0;int L[N],R[N];int dp[N][20],deep[N];void dfs(int u,int fa,int dep)//dfs序+树上倍增
{max_deep=max(max_deep,dep);L[u]=++cnt;d[dep].push_back(cnt);deep[u]=dep;dp[u][0]=fa;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==fa)continue;dfs(v,u,dep+1);}R[u]=cnt;
}int solve(int u,int p)
{int h=deep[u];for(int i=0;i<=limit;i++)//先跳到祖先p的位置if((p>>i)&1)u=dp[u][i];if(u==0)//若跳到0节点了,说明祖先不存在,直接返回0return 0;int a=L[u],b=R[u];//查找deep[u]层在[a,b]区间内有多少个节点int l=lower_bound(d[h].begin(),d[h].end(),a)-d[h].begin();int r=upper_bound(d[h].begin(),d[h].end(),b)-d[h].begin()-1;return r-l;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);scanf("%d",&n);limit=log2(n)+1;for(int i=1;i<=n;i++){int num;scanf("%d",&num);if(num)node[num].push_back(i);elseroot.push_back(i);}for(int i=0;i<root.size();i++)dfs(root[i],0,0);for(int i=0;i<=max_deep;i++)sort(d[i].begin(),d[i].end());int w;scanf("%d",&w);while(w--){int a,b;scanf("%d%d",&a,&b);printf("%d ",solve(a,b));}return 0;
}

树上启发式合并:

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e5+100;int deep[N],son[N],num[N],dp[N][20],ans[N],cnt[N],limit;bool vis[N];vector<int>node[N];vector<pair<int,int>>Q[N];//<id,deep>void dfs_son(int u,int dep)
{son[u]=-1;num[u]=1;deep[u]=dep;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){dfs_son(v,dep+1);num[u]+=num[v];if(son[u]==-1||num[son[u]]<num[v])son[u]=v;}
}void cal(int u,int val)
{cnt[deep[u]]+=val;for(auto v:node[u]){if(vis[v])continue;cal(v,val);}
}void dfs(int u,int keep)
{for(auto v:node[u]){if(v==son[u])continue;dfs(v,0);}if(son[u]!=-1){dfs(son[u],1);vis[son[u]]=true;}cal(u,1);for(auto i:Q[u]){int id=i.first;int d=i.second;ans[id]=cnt[d];}if(son[u]!=-1)vis[son[u]]=false;if(!keep)cal(u,-1);
}int get_Ancestor(int x,int d)
{for(int i=0;i<=limit;i++)if((d>>i)&1)x=dp[x][i];return x;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);int n,m;scanf("%d",&n);limit=log2(n)+1;for(int i=1;i<=n;i++){int fa;scanf("%d",&fa);node[fa].push_back(i);dp[i][0]=fa;}dfs_son(0,0);scanf("%d",&m);for(int i=1;i<=m;i++){int u,d;scanf("%d%d",&u,&d);if(deep[u]<=d)ans[i]=1;else{int fa=get_Ancestor(u,d);Q[fa].push_back(make_pair(i,deep[u]));}}dfs(0,1);for(int i=1;i<=m;i++)printf("%d ",ans[i]-1);return 0;
}

CodeForces - 208E Blood Cousins(树上倍增+二分/树上启发式合并)相关推荐

  1. CodeForces - 246E Blood Cousins Return(树上启发式合并)

    题目链接:点击查看 题目大意:给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor,每个节点有名字,名字不唯一.多次查询,给出 u 和 k,问以 u 为根节点的子树下 ...

  2. CF 208E Blood Cousins (dus on tree)

    题目链接:https://codeforces.com/contest/208/problem/E 题意:n个节点,组成森林.m个询问,一个询问由(v,p)组成,问v的p-cousin有多少,v的p- ...

  3. codeforces 549F Yura and Developers(分治、启发式合并)

    codeforces 549F Yura and Developers 题意 给定一个数组,问有多少区间满足:去掉最大值之后,和是k的倍数. 题解 分治,对于一个区间,找出最大值之后,分成两个区间. ...

  4. 对LCA、树上倍增、树链剖分(重链剖分长链剖分)和LCT(Link-Cut Tree)的学习

    LCA what is LCA & what can LCA do LCA(Lowest Common Ancestors),即最近公共祖先 在一棵树上,两个节点的深度最浅的公共祖先就是 L ...

  5. 【bzoj4009】[HNOI2015]接水果 DFS序+树上倍增+整体二分+树状数组

    题目描述 给出一棵n个点的树,给定m条路径,每条路径有一个权值.q次询问求一个路径包含的所有给定路径中权值第k小的. 输入 第一行三个数 n和P 和Q,表示树的大小和盘子的个数和水果的个数. 接下来n ...

  6. CodeForces - 1535E Gold Transfer(树上倍增+交互)

    题目链接:点击查看 题目大意:给出一棵初始时只有一个点的树,每个点都有两个值:ai,cia_i,c_iai​,ci​,分别代表黄金的个数和单价.需要执行 mmm 次操作,每次操作分为两种类型: 1pi ...

  7. 主席树 + 树上倍增 ---- codeforces 587C[树上倍增或者主席树]

    题目链接 给定一棵n个点的树,给定m个人(m≤n)在哪个点上的信息,每个点可以有任意个人:然后给q个询问,每次问u到v上的路径有的点上编号最小的k(k≤10)个人(没有那么多人就该有多少人输出多少人) ...

  8. CodeForces - 609E Minimum spanning tree for each edge(最小生成树+树链剖分+线段树/树上倍增)

    题目链接:点击查看 题目大意:给出一张 n 个点和 m 条边组成的无向图,现在询问包含每一条边的最小生成树 题目分析:考虑求解次小生成树的思路: 求出最小生成树 ans 枚举每一条非树边 ( u , ...

  9. 树上倍增法求最近公共祖先LCA

    LCA,最近公共祖先,这个东西有很多作用,因此,如何高效求出LCA就成了一个热点的讨论话题. 下面所有的讨论都以图中这棵树为例子. 先来了解下什么是倍增吧,倍增其实就是二分的逆向,二分是逐渐缩小范围, ...

最新文章

  1. 兼容IE各版本的纯CSS二级下拉菜单
  2. Navicat premiu的导入和导出
  3. excel分类_Excel小技巧61:将输入的数字显示为中文
  4. 文件格式和扩展名不匹配。文件可能已损坏或不安全。除非您信任其来源,否则请勿打开。是否仍要打开它?
  5. HTML连接共享文件,怎么连接共享文件夹
  6. 数据科学之——大数据体系
  7. Dev gridView中设置自适应列宽和日期显示格式、金额的显示格式
  8. Unicode汉字编码表
  9. 有线异步通信原理_【对讲机的那点事】揭秘对讲机系统集群网关的应用原理
  10. 商品档案,文具五金服装鞋帽酒店烟酒饰品,日用百货超市,批发零售进销存收银财务一体管理软件
  11. 使用osgeo的GDAL获取tiff文件坐标
  12. 基于python的安全即时通讯系统
  13. 【资源分享】云计算 CRM 等网上搜集资料
  14. FontAwesome图标选择器
  15. AD20常用快捷键汇总
  16. anaconda打不开怎么办
  17. 解决:The ‘Access-Control-Allow-Origin‘ header contains___Nginx跨域设置
  18. tomcat重启警告:Abandoned connection cleanup thread)
  19. 【计算机网络】时延、发送时延、传输时延、处理时延、排队时延、时延带宽积
  20. 怎么将heic转为jpg格式,哪个图片转换器好用

热门文章

  1. MySQL总结连接查询
  2. SIMPLE/REUSE/BATCH 三种执行器的区别?
  3. 将session维护在客户端
  4. weblogic 扫描不到jar包的类问题解决方案
  5. web服务器软件_概述
  6. 为什么要用RabbitMQ
  7. Oracle之事务和锁
  8. 使用Github(创建仓库、仓库主页说明)
  9. dw html 并排三个正方形边框,dw网页设计与制作题库(单选题)(43页)-原创力文档...
  10. JAVA浮点数使用陷阱:2.7 和 8.1 / 3 比较