HDU 4605 主席树

题意:

题目链接:http://acm.split.hdu.edu.cn/submit.php?pid=4605
一棵二叉树,根节点为1,每个节点都有一个权值,给出q个询问,每次询问给出v和x,问数字x从根节点落下,到节点v的概率是多少。
其中数x假如到达一个节点u:
1. 如果x==w[u],则x留在该节点。
2. 如果x>w[u],则x有1/8概率往左子树走,有7/8概率往右子树走。
3. 如果x>w[u],则x有1/2概率往左子树走,有1/2概率往右子树走。


思路:

主席树,要知道最后的答案,只要知道在从根到v的路径中,有多少数比x小,而且要分向左走和向右走分别有多少个点比x小。
根据树的结构建立主席树,每次在父亲的版本上建立新的线段树。树上要保存两个权值,一个是向左走的,一个是向右走的。
因为一个m写成了n,T了一万年。


代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 10;

struct node {
    int ls, rs, sum[2];
} ns[MAXN * 20];

int rt[MAXN], ct;

void build(int& now, int l, int r) {
    now = ++ct;
    ns[now].sum[0] = ns[now].sum[1] = 0;
    if (l == r) return;
    int m = (l + r) >> 1;
    build(ns[now].ls, l, m);
    build(ns[now].rs, m + 1, r);
}

void update(int& now, int old, int l, int r, int x, int y) {
    now = ++ct;
    ns[now] = ns[old];
    if (l == r) {
        ns[now].sum[y]++;
        return;
    }
    int m = (l + r) >> 1;
    if (x <= m) update(ns[now].ls, ns[old].ls, l, m, x, y);
    else update(ns[now].rs, ns[old].rs, m + 1, r, x, y);
    ns[now].sum[y] = ns[ns[now].ls].sum[y] + ns[ns[now].rs].sum[y];
}

int query(int now, int old, int l, int r, int L, int R, int y) {
    if (L <= l && r <= R) return ns[now].sum[y] - ns[old].sum[y];
    int m = (l + r) >> 1, res = 0;
    if (L <= m) res += query(ns[now].ls, ns[old].ls, l, m, L, R, y);
    if (R > m) res += query(ns[now].rs, ns[old].rs, m + 1, r, L, R, y);
    return res;
}

int n, t, m;
int in[MAXN], a[MAXN], b[MAXN], dis[MAXN];

struct Node {
    int l, r;
} tree[MAXN];

void dfs(int u, int d) {
    int l = tree[u].l, r = tree[u].r;
    dis[u] = d;
    int gg = lower_bound(b + 1, b + 1 + n, a[u]) - b;
    if (l != -1) {
        update(rt[l], rt[u], 1, n, gg, 0);
        dfs(l, d + 1);
    }
    if (r != -1) {
        update(rt[r], rt[u], 1, n, gg, 1);
        dfs(r, d + 1);
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            tree[i].l = tree[i].r = -1;
            b[i] = a[i];
        }
        sort (b + 1, b + 1 + n);
        //n = unique(b + 1, b + 1 + n) - b - 1;
        scanf("%d", &t);
        for (int i = 1; i <= t; i++) {
            int u, ls, rs;
            scanf("%d%d%d", &u, &ls, &rs);
            tree[u].l = ls; tree[u].r = rs;
        }
        scanf("%d", &m);
        ct = 0;
        build(rt[1], 1, n);
        dfs(1, 0);
        for (int i = 1; i <= m; i++) {
            int v, x;
            scanf("%d%d", &v, &x);
            int xx = lower_bound(b + 1, b + 1 + n, x) - b;
            if (b[xx] == x) {
                int t1 = query(rt[v], rt[1], 1, n, xx, xx, 0);
                if (t1 > 0) {
                    puts("0");
                    continue;
                }
                int t2 = query(rt[v], rt[1], 1, n, xx, xx, 1);
                if (t2 > 0) {
                    puts("0");
                    continue;
                }
            }
            if (xx > n || b[xx] > x) --xx;
            int lnum = xx >= 1 ? query(rt[v], rt[1], 1, n, 1, xx, 0) : 0;
            int rnum = xx >= 1 ? query(rt[v], rt[1], 1, n, 1, xx, 1) : 0;
            int ansx = rnum, ansy = lnum * 3 + rnum * 3 + (dis[v] - rnum - lnum);
            printf("%d %d\n", ansx, ansy);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值