P5384 [Cnoi2019] 雪松果树

P5384 [Cnoi2019] 雪松果树

题目背景

幻想乡,冬。

一年一度,生长在高山上的雪松果树又结果了。

Cirno 不知从哪弄到了 1,2,39 颗雪松果,然后很开心的吃掉了其中 6 颗,最后还剩最后 1 颗。

Cirn o因为以后吃不到雪松果而感到忧愁,于是决定种在美丽的雾之湖畔。

第一天,发芽。

第二天,雪松果树长成了一颗参天大树, 上面长满了雪松果。

Cirno 在雪松果成熟之前早有一些问题想知道,但现在她忙于收集雪松果,就把问题丢给了你。

题目描述

雪松果树是一个以 1 为根有着 N 个节点的树。

除此之外, Cirno还有 Q 个询问,每个询问是一个二元组 (u,k) ,表示询问 u 节点的 k-cousin 有多少个。

我们定义:

节点 u1-father 为 路径 (1,u) (不含 u)上距 u 最近的节点

节点 uk-father 为 节点 「u(k1)-father」 的 1-father

节点 uk-son 为所有 k-father 为 u 的节点

节点 uk-cousin 为 节点「 uk-father」的 k-son (不包含 u 本身)

数据范围:

对于 100% 的数据 N,Q106

Solution:

说句闲话:

感谢 red_fire 大神的推荐。

看题面时:水题。
口胡时:水题。
然而爆唐的我本人总能把自己口胡的思路实现得爆炸就是了。

言归正传:

我们要实现的东西其实很简单,就是对于一个点 x 求出他的树上 k 级祖先,然后查询他的 k 级祖先子树下深度与 x 相等的节点个数,然后再把 x 扣掉就好了。

实现:

我们可以维护一个桶 baci 表示深度为 i 的节点当前有多少个。由于我们只统计当前子树内的,所以我们要做一个差分,在访问到当前节点 x 且未访问其后代时先减去桶中原有的值,在访问完该节点的后代之后再加上桶中的值,就得到了该子树下对应的桶中的值。

所以我们要将询问离线并挂到 xk 级祖先上,在 dfs 时直接查询。

Code:

#include<bits/stdc++.h>
const int lg=20;
const int N=1e6+6;
using namespace std;
int n,m,cnt;
int dep[N],bac[N],f[N][lg+1],bit[lg+5],ans[N];
vector<int> E[N];
vector<tuple<int,int> > Q[N];
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;
for(auto [k,id] : Q[x]){ans[id]-=bac[dep[x]+k];}
for(int y : E[x])dfs(y,x);bac[dep[x]]++;
for(auto [k,id] : Q[x]){ans[id]+=bac[dep[x]+k];}
}
inline int k_fa(int x,int k){ for(int i=lg;i>=0;i--)if(k>=bit[i])k-=bit[i],x=f[x][i];return x;}
void work()
{
cin>>n>>m;bit[0]=1;for(int i=1;i<=lg;i++)bit[i]=bit[i-1]<<1;
for(int u=2;u<=n;u++){scanf("%d",&f[u][0]);E[f[u][0]].emplace_back(u);}
for(int x=1;x<=n;x++)for(int j=1;j<=lg;j++)f[x][j]=f[f[x][j-1]][j-1];
for(int i=1,x,k;i<=m;i++)
{
scanf("%d%d",&x,&k);
int y=k_fa(x,k);ans[i]=-1;
if(!y||k==0){ans[i]=0;continue;}
Q[y].emplace_back(k,i);
}
dfs(1,0);
for(int i=1;i<=m;i++)printf("%d ",ans[i]);
}
int main()
{
work();return 0;
}
posted @   liuboom  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示