HRBUST 2386 - 霍冰阔落(主席树)

Description

给出一棵规模为n的有根树代表族谱,1是祖先。对于每个人,我们都知道他的父母是几号。当然除了1。。我们假设1是直接进化来的。。

当然以树为表示的族谱不是生物遗传的那样的,每个点的父亲就是他这个人的父亲,他的所有儿子节点全都是他的孩子。不要纠结男女的问题。。

如果有一天,一个人突然突发奇想,想知道自己的第x代的后代到第y代的后代一共有多少人怎么办QAQ,编程解决这个问题把!

Input

第一行一个t t<=5,代表接下来的数据组数

对于每一组,第一行是一个整数n n<=100000代表族谱上的人数。

第二行是n-1个数字,第i个数字代表i+1这个人的父亲是几号。

第三行是一个整数q,q<=100000 代表询问的个数。

接下来的q行,每行三个数字id,x,y id,x,y<=n 代表询问的人,x,y如题意,一个人的第1代后代是他的儿子或女儿。

Output

对于每一组输出q行,代表这q个询问的答案。

Sample Input

1

3

1 2

3

1 1 2

2 1 2

3 1 2

Sample Output

2

1

0

Source

“科林明伦杯”计算机学院2018年程序设计竞赛


解题分析

跑一遍dfs,统计dfs序 st s t ,子树终点 ed e d ,深度 dep d e p

询问id的第x代到第y代总数,即求id的所有后代 (st[id],ed[id]] ( s t [ i d ] , e d [ i d ] ] 中,深度为 [dep[id]+x,dep[id]+y] [ d e p [ i d ] + x , d e p [ i d ] + y ] 的个数。可依dfs序,以深度为权值建主席树, O(log(n)) O ( l o g ( n ) ) 回答单次询问。

总时间复杂度 O(qlog(n)) O ( q l o g ( n ) )


AC代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
using namespace std;

inline int read()
{
    int x = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

const int MAXN = 100010;

int n, m, id, dep[MAXN], st[MAXN], ed[MAXN];
struct Edge{int to, next;}edges[MAXN];
int head[MAXN], tot;

void AddEdge(int u, int v)
{
    edges[++ tot].to = v; edges[tot].next = head[u]; head[u] = tot;
}

void dfs(int x, int num)
{
    st[x] = ++ id;
    dep[id] = num;
    for(int i = head[x]; i != -1; i = edges[i].next) dfs(edges[i].to, num + 1);
    ed[x] = id;
}

struct PresidentTree{int lc, rc, sum;}pt[MAXN * 22];
int cnt, rt[MAXN];
#define lc(x) pt[x].lc
#define rc(x) pt[x].rc
#define sum(x) pt[x].sum
#define mid ((l + r) >> 1)

void build(int &k1, int l, int r)
{
    k1 = ++ cnt;
    if(l == r) return;
    build(lc(k1), l, mid);
    build(rc(k1), mid + 1, r);
}

void modify(int &k1, int pre, int l, int r, int v)
{
    k1 = ++ cnt; pt[k1] = pt[pre]; ++ sum(k1);
    if(l == r) return;
    if(v <= mid) modify(lc(k1), lc(pre), l, mid, v);
    else modify(rc(k1), rc(pre), mid + 1, r, v);
}

int query(int k1, int pre, int l, int r, int ql, int qr)
{
    if(ql <= l && r <= qr) return sum(k1) - sum(pre);
    int ans = 0;
    if(ql <= mid) ans += query(lc(k1), lc(pre), l, mid, ql, qr);
    if(qr  > mid) ans += query(rc(k1), rc(pre), mid + 1, r, ql, qr);
    return ans;
}

int main()
{
    int T = read();
    while(T --){
        memset(head, -1, sizeof(head));
        id = cnt = tot = 0;
        n = read();
        REP(i, 2, n){
            int fa = read();
            AddEdge(fa, i);
        }
        dfs(1, 1);
        build(rt[0], 1, n);
        REP(i, 1, n) modify(rt[i], rt[i - 1], 1, n, dep[i]);
        m = read();
        REP(i, 1, m){
            int no = read(), d1 = read(), d2 = read();
            d1 += dep[st[no]]; d2 += dep[st[no]];
            printf("%d\n", query(rt[ed[no]], rt[st[no]], 1, n, d1, d2));
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值