P5384 [Cnoi2019] 雪松果树 (找u的k级祖先+树上差分)

本题 trick : o(n) 求k级祖先 ,树上差分
把题目拆成两个部分
1.首先 把u的贡献放到其 k级祖先上,这一部分通过栈来维护每个点的祖先来实现 复杂度 o(n)。为啥这个题要这么做呢?因为通过倍增上去找k级父亲会 T。
2.第二部解决 u的 k级儿子问题,这一部分可以通过树上差分来实现, 具体做法是用桶来维护每一层的数量,然后在第一次访问节点时把对应层数节点的贡献减去然后在整个子树遍历完了之后在加上对应层数节点的贡献加上。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int>pir;
const ll INF = 1e18;
const int N = 1e6 + 5;

std::vector<int> g[N];
std::vector<pir> q[N], h[N];
int st[N], dep[N];
int ans[N], cnt[N];
int top;
void dfs1(int u)
{
    st[++top] = u;
    for(int v : g[u])
    {
        dep[v] = dep[u] + 1;
        dfs1(v);
    }
    for(auto [k, id] : q[u])
    {
        if(dep[u] < k)ans[id] = 1;
        else h[st[top - k]].push_back({id, dep[u]});
    }
    --top;
}
void dfs(int u)
{
    for(auto [id, k] : h[u])ans[id] = -cnt[k];
    cnt[dep[u]]++;
    for( auto v : g[u])
    {
        dfs(v);
    }
    for(auto [id, k] : h[u])ans[id] += cnt[k];
}
void solve()
{
    int n, m;
    cin >> n >> m;
    for(int i = 2; i <= n; i++)
    {
        int fa;
        cin >> fa;
        g[fa].push_back(i);
    }
    for(int i = 1; i <= m; i++)
    {
        int u, k;
        cin >> u >> k;
        q[u].push_back({k, i});
    }
    dfs1(1);
    top = 0;
    dfs(1);
    for(int i = 1; i <= m; i++)cout << ans[i] - 1 << " ";
    cout << '\n';
}

int main()
{

    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);

    solve();
}
posted @   pipipipipi43  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示