CF208E Blood Cousins 题解
一个奇奇怪怪的复杂度很垃圾的线段树合并解法
通过分析可以发现,要找$x$的$k$辈兄弟,只需要找到$x$的$k$辈祖先,然后查找以该祖先为根的子树中和$x$深度相同的节点个数$-1$即可。也就是说,询问只与深度有关,与具体是哪个节点无关。
具体怎么实现呢?想到dfs处理,显然在遍历过$x$后会回溯到$x$的$k$辈祖先,因此有一个想法,就是在回溯到$x$的$k$辈祖先时执行查询对应的查询就可以了,这样是一个离线的做法。目前的问题就是,如何能在$x$的$k$辈祖先时执行查询。
有一个做法就是提前处理出$x$的祖先,然后把询问插入到$x$的$k$辈祖先处,等到回溯到的时候进行查询;另一种就是一种比较奇奇怪怪的做法,利用大根堆维护询问,把祖先节点的深度作为关键字,当遍历到一个节点时,处理深度相同的询问。具体询问用线段树合并就可以处理了。
还有一个可以优化的地方,如果有祖先节点和查询节点的深度都相同的询问,答案相同,不需要重复查询。
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+100;
struct Seg
{
int lson,rson,sum;
#define lson(i) t[i].lson
#define rson(i) t[i].rson
#define sum(i) t[i].sum
}t[5000000];
struct Que
{
int fa,son,id;//祖先深度,查询节点深度,询问编号
};
int n,m,tot,cnt;
int head[N],ver[N],Next[N];
int r[N],ans[N];
bool v[N];
priority_queue<Que> q;//维护询问
vector<pair<int,int> > query[N];//存储询问
bool operator<(const Que &a,const Que &b)
{
return a.fa==b.fa?a.son<b.son:a.fa<b.fa;//注意是小于号
}//大根堆
void add(int x,int y)
{
ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
int merge(int x,int y)
{
if(!x)
return y;
if(!y)
return x;
sum(x)+=sum(y);
lson(x)=merge(lson(x),lson(y));
rson(x)=merge(rson(x),rson(y));
return x;
}//线段树合并
int ask(int &p,int l,int r,int k)
{
if(!p)
p=++cnt;
if(l==r)
return sum(p);
int mid=(l+r)>>1;
if(k<=mid)
return ask(lson(p),l,mid,k);
else
return ask(rson(p),mid+1,r,k);
}//查询
void change(int &p,int l,int r,int k)
{
if(!p)
p=++cnt;
if(l==r)
{
sum(p)++;
return ;
}
int mid=(l+r)>>1;
if(k<=mid)
change(lson(p),l,mid,k);
else
change(rson(p),mid+1,r,k);
}//修改
void solve(int x,int d)
{
v[x]=1;//访问数组
for(int i=head[x];i;i=Next[i])
{
int y=ver[i];
solve(y,d+1);
r[x]=merge(r[x],r[y]);//子节点合并
}
while(q.size() && q.top().fa==d)//处理深度相同的询问
{
int y=q.top().son,id=q.top().id;q.pop();
ans[id]=ask(r[x],1,1e5,y)-1;//查询
while(q.size() && q.top().fa==d && q.top().son==y)
{
ans[q.top().id]=ans[id];
q.pop();
}//询问等价不用重复查询
}
change(r[x],1,1e5,d);//修改
for(int i=0;i<query[x].size();i++)
if(d-query[x][i].first>0)//保证询问合法
{
Que now;
now.fa=d-query[x][i].first;
now.son=d;
now.id=query[x][i].second;
q.push(now);
}//将询问插入大根堆
}
int main()
{
scanf("%d",&n);cnt=n;
for(int i=1,x;i<=n;i++)
{
scanf("%d",&x);r[i]=i;
if(x)
add(x,i);
}
scanf("%d",&m);
for(int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
query[x].push_back(make_pair(y,i));//插入询问
}
for(int i=1;i<=n;i++)
if(!v[i])
solve(i,1);
for(int i=1;i<=m;i++)
printf("%d ",ans[i]);
return 0;
}
CF208E Blood Cousins 题解的更多相关文章
- CF208E Blood Cousins
Blood Cousins 题目描述 小C喜欢研究族谱,这一天小C拿到了一整张族谱. 小C先要定义一下k-祖先. x的1-祖先指的是x的父亲 x的k-祖先指的是x的(k-1)-祖先的父亲 小C接下来要 ...
- CF208E Blood Cousins(DSU,倍增)
倍增求出祖先,\(\text{DSU}\)统计 本来想用树剖求\(K\)祖,来条链复杂度就假了 #include <cstring> #include <cstdio> #in ...
- CF 208E - Blood Cousins dfs序+倍增
208E - Blood Cousins 题目:给出一棵树,问与节点v的第k个祖先相同的节点数有多少个. 分析: 寻找节点v的第k个祖先,这不就是qtree2简化版吗,但是怎么统计该祖先拥有多少个深度 ...
- [cf contest246] E - Blood Cousins Return
[cf contest246] E - Blood Cousins Return time limit per test 3 seconds memory limit per test 256 meg ...
- Codeforces 246E - Blood Cousins Return (树上启发式合并)
246E - Blood Cousins Return 题意 给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor,每个节点有名字,名字不唯一.多次查询,给出 u k ...
- Codeforces 208E - Blood Cousins(树上启发式合并)
208E - Blood Cousins 题意 给出一棵家谱树,定义从 u 点向上走 k 步到达的节点为 u 的 k-ancestor.多次查询,给出 u k,问有多少个与 u 具有相同 k-ance ...
- Codeforces 246E Blood Cousins Return(树上启发式合并)
题目链接 Blood Cousins Return #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) f ...
- 【CF208E】Blood Cousins
题目大意:给定一个 N 个点的森林,M 个询问,每次询问对于点 u 来说,有多少个点和 u 有相同的 K 级祖先. 题解:线段树合并适合处理子树贡献的问题. 发现要回答这个询问在点 u 处计算很困难, ...
- CF 208E. Blood Cousins [dsu on tree 倍增]
题意:给出一个森林,求和一个点有相同k级祖先的点有多少 倍增求父亲然后和上题一样还不用哈希了... #include <iostream> #include <cstdio> ...
随机推荐
- PHP touch() 函数
定义和用法 touch() 函数设置指定文件的访问和修改时间. 如果成功,该函数返回 TRUE.如果失败,则返回 FALSE. 语法 touch(filename,time,atime) 参数 描述 ...
- asp.net mvc 模拟百度搜索
页面代码: <td><span>*</span>车牌号码:</td> <td> <div id="search"& ...
- 4.15 省选模拟赛 编码 trie树 前缀和优化建图 2-sat
好题 np. 对于20分 显然可以爆搜. 对于50分 可以发现每个字符串上的问号要么是0,要么是1.考虑枚举一个字符串当前是0还是1 这会和其他字符串产生矛盾. 所以容易 发现这是一个2-sat问题. ...
- requests入门实践02_下载斗图拉最新表情包
新版本移步:https://www.cnblogs.com/zy7y/p/13376228.html 下载斗图拉最新表情包 要爬取的目标所在网址:http://www.doutula.com/phot ...
- python IF while逻辑判断语句
if判断语句 if 1==1 and 2==2: pass else: print('error') if 1==1 or 2==2: pass else: print('error') while循 ...
- 她娇羞道“不用这样细致认真的说啊~~”———详细图解在Linux环境中创建运行C程序
她娇羞说,不用这样细致认真的说啊———详细图解在Linux环境中创建运行C程序“不,这是对学习的负责”我认真说到 叮叮叮,停车,让我们看看如何在Linux虚拟机环境中,创建运行C程序 详细图解在Lin ...
- “随手记”开发记录day04
今天完成了添加收入和支出的页面,其实挺简单的就是里面的那个图表有些难搞,你得把每个图标和文字对应起来 挺费事的 话不多说,上效果 其中点击旋转按钮转换收入支出是我们找了好久才找出来这个方法的,太不容易 ...
- 14、Java文件操作stream、File、IO
1.文件操作涉及到的基本概念 File File类 是文件操作的主要对象中文意义就是 文件 顾名思意 万物皆文件,在计算上看到的所有东西都是文件保存,不管是你的图片.视频.数据库数据等等都是按照基本的 ...
- 使用webgl(three.js)创建科技版3D机房,3D机房微模块详细介绍(升级版三)—— 1
上节课已经详细描述了微模块机房的实现过程,文章地址(https://www.cnblogs.com/yeyunfei/p/10484241.html) 紧接着上节课的内容 我们这节可来详细讲解科技版机 ...
- Java对象与类—对象与类
1.类 类(class)是构造对象的模板,具体点说类就是对具有相同性质,相同行为的一群对象的抽象说明.由类构造(construst)对象的过程称为创建类的实例(instance). 2.对象 对象是类 ...