传送门

题目大意:

小C喜欢研究族谱,这一天小C拿到了一整张族谱。

小C先要定义一下k-祖先。

  • x的1-祖先指的是x的父亲
  • x的k-祖先指的是x的(k-1)-祖先的父亲

小C接下来要定义k-兄弟

  • x的k-兄弟指的是与x的k-祖先相同的人
  • 如果不存在k-祖先那么x没有k-兄弟

小C想问问你,x到底有多少k-兄弟?小C打算问Q次这样的问题。

数据范围:

$n<=10^5,Q<=10^5$

$dsu\ on\ tree$ 基础题,当然也有显然的在线做法

对于每个询问 $(x,k)$,不妨转换为 $(u,v)$ ,表示求 $u$ 的子树中,与 $v$ 深度相同的节点数

考虑怎么离线搞,直接 $dsu\ on\ tree$ 维护一个统计各个深度节点数的数组 $cnt$ 即可

每次 $dfs$ 时最后 $dfs$ 重儿子,从而保留重儿子的 $cnt$ ,这样与每个节点 $u$ 有关的询问只要暴力枚举轻儿子子树即可

复杂度就是启发式合并的 $nlog_n$

具体看代码,注意数据是森林

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
inline int read()
{
int x=,f=; char ch=getchar();
while(ch<''||ch>'') { if(ch=='-') f=-; ch=getchar(); }
while(ch>=''&&ch<='') { x=(x<<)+(x<<)+(ch^); ch=getchar(); }
return x*f;
}
const int N=2e6+;
int fir[N],from[N<<],to[N<<],cntt;
inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; }
int f[N][],dep[N],son[N],sz[N];//f是倍增数组,dep是节点深度,son是重儿子,sz是子树大小
void dfs1(int x)//预处理上面四个数组
{
dep[x]=dep[f[x][]]+; sz[x]=; int mx=;
for(int i=;i<=;i++) f[x][i]=f[f[x][i-]][i-];
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(v==f[x][]) continue;
f[v][]=x; dfs1(v);
if(sz[v]>sz[son[x]]) son[x]=v;
sz[x]+=sz[v];
}
}
struct dat{
int x,y,id;
inline bool operator < (const dat &tmp) const {
return x<tmp.x;
}
}d[N];//存询问
int n,Q;
int cnt[N],ans[N],id[N],dfs_clock;//id是dfs序为i的节点编号
void dfs2(int x,bool flag)//flag判断是否保留cnt
{
id[++dfs_clock]=x; int L=dfs_clock;//轻儿子子树dfs序的左区间
if(!son[x]) { if(flag) cnt[dep[x]]++; return; }
for(int i=fir[x];i;i=from[i])
{
int &v=to[i]; if(v==f[x][]||v==son[x]) continue;
dfs2(v,);//走轻儿子
}
int R=dfs_clock,t=lower_bound(d+,d+Q+,(dat){x,,})-d;//R是轻儿子子树右区间,t是第一个u为x的询问的位置
dfs2(son[x],);//最后走重儿子并保留cnt
for(int i=L;i<=R;i++) cnt[ dep[id[i]] ] ++;//枚举轻儿子子树,更新cnt
for(int i=t;d[i].x==x;i++) { ans[d[i].id]=cnt[dep[d[i].y]]-; }//更新ans
if(!flag) for(int i=L;i<=dfs_clock;i++) cnt[ dep[id[i]] ]--;//清空cnt
}
int rt[N],tot;//注意是森林,要存每个数的根
int main()
{
n=read(); int a,b;
for(int i=;i<=n;i++)
{
a=read();
add(a,i);
if(!a) rt[++tot]=i;
}
for(int i=;i<=tot;i++) dfs1(rt[i]);
Q=read();
for(int i=;i<=Q;i++)
{
a=read(),b=read(); int t=a;
for(int j=;j>=;j--) if(b&(<<j)) t=f[t][j];
d[i].x=t,d[i].y=a,d[i].id=i;
}
sort(d+,d+Q+);
for(int i=;i<=tot;i++) dfs2(rt[i],);
for(int i=;i<=Q;i++) printf("%d ",ans[i]);
return ;
}

Codeforces 208E. Blood Cousins的更多相关文章

  1. Codeforces 208E - Blood Cousins(树上启发式合并)

    208E - Blood Cousins 题意 给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor.多次查询,给出 u k,问有多少个与 u 具有相同 k-ance ...

  2. CF 208E - Blood Cousins dfs序+倍增

    208E - Blood Cousins 题目:给出一棵树,问与节点v的第k个祖先相同的节点数有多少个. 分析: 寻找节点v的第k个祖先,这不就是qtree2简化版吗,但是怎么统计该祖先拥有多少个深度 ...

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

    246E - Blood Cousins Return 题意 给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor,每个节点有名字,名字不唯一.多次查询,给出 u k ...

  4. Codeforces 246E Blood Cousins Return(树上启发式合并)

    题目链接 Blood Cousins Return #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) f ...

  5. CF 208E. Blood Cousins [dsu on tree 倍增]

    题意:给出一个森林,求和一个点有相同k级祖先的点有多少 倍增求父亲然后和上题一样还不用哈希了... #include <iostream> #include <cstdio> ...

  6. [cf contest246] E - Blood Cousins Return

    [cf contest246] E - Blood Cousins Return time limit per test 3 seconds memory limit per test 256 meg ...

  7. CF208E Blood Cousins

    Blood Cousins 题目描述 小C喜欢研究族谱,这一天小C拿到了一整张族谱. 小C先要定义一下k-祖先. x的1-祖先指的是x的父亲 x的k-祖先指的是x的(k-1)-祖先的父亲 小C接下来要 ...

  8. CF 246E. Blood Cousins Return [dsu on tree STL]

    题意: 一个森林,求k级后代中多少种不同的权值 用set维护每个深度出现的权值 一开始一直在想删除怎么办,后来发现因为当前全局维护的东西里都是当前子树里的,如果要删除那么当前一定是轻儿子,直接清空se ...

  9. codeforces246E Blood Cousins Return

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

随机推荐

  1. Linq 高级应用实例

    using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Tex ...

  2. process-hacker

    https://github.com/processhacker/processhacker#process-hacker // begin_phapppub typedef enum _PH_KNO ...

  3. rabbitma客户端

    知道答案了,原因是重连的时候,每次重连都创建了一个新的线程,然后有信号的时候,每个线程都连接到rabbitMq服务器上去,导致了同一个IP通过不同端口链接上了服务器,出现了多个channel,而发多次 ...

  4. wine安装与配置

      1. 安装wine32dpkg --add-architecture i386 && apt-get update && apt-get install wine3 ...

  5. poj 3468 : A Simple Problem with Integers 【线段树 区间修改】

    题目链接 题目是对一个数组,支持两种操作 操作C:对下标从a到b的每个元素,值增加c: 操作Q:对求下标从a到b的元素值之和. #include<cstdio> #include<c ...

  6. 【leetcode】870. Advantage Shuffle

    题目如下: 解题思路:几千年前,一个古人曾经解过这个题目,他的名字叫做田忌,后人称他的解题思想叫做“田忌赛马”.言归正传,本题就是一个田忌赛马的问题,先将A与B进行排序,然后判断A[0]与B[0]的大 ...

  7. drf 的分页功能

    1 settings中配置 page_size = 20 代表每页20条数据 REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES': ( 'rest_framewor ...

  8. ChainMap简单示例

    ChainMap是dict的子类,拥有dict的所有功能, 感觉用它的地方吧??? from collections import ChainMap """ 相当于joi ...

  9. MaxCompute按量计费计算任务消费监控告警

    MaxCompute 按量计费资源为弹性伸缩资源,对于计算任务,按任务需求提供所需资源,对资源使用无限制,同时MaxCompute按量计费的账单为天账单,即当天消费需要第二天才出账,因此,有必要对计算 ...

  10. 台哥原创:java 数独源码

    2010年,当时正在做手机游戏的客户端开发工作. 每天加班之余,用了两三个晚上,开发了这个数独. 主要是生成数独数组的算法,有点难度.. ​ 如下图:点选数字栏里的数字后,界面上所有该数字会高亮显示. ...