HDU-4605 Magic Ball Game

题意:给定一颗以1为根的数,每个节点要么有两个孩子节点,要么没有孩子,每个节点有一个重量,现在从节点1往下放置一个小球,根据小球和节点的重量的不同球落下的轨迹是一个概率问题:

设球的重量为X,节点的重量为w[i]:
X = w[i],那么小球的运动将停止;
X < w[i],那么小球向左孩子下落的概率为1/2,向右孩子下落的概率为1/2;
X > w[i],那么小球向左落下概率为1/8,向右落下的概率为7/8。

现在有Q组询问,问小球的质量为X,落到v节点的概率为多大?

分析:最直接的办法就是直接暴力求解该题,从询问的叶子节点开始向上寻找,进行概率的累加,比赛的时候这样写,超时了。赛后听说是使用的树状数组维护路径状态进行求解。具体过程是在一个dfs的过程中,统计好当前位置的左路径的节点和右路径的节点,然后将小球的质量在树状数组中进行查找,计算出比小球质量较小的节点数以及比小球质量较大的节点数,累加概率即可。注意直接dfs会爆栈,使用编译器命令后解决该问题。

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <map>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

struct Node {
    int num, vertex, weight, ansx, ansy;
    Node(int _n, int _v, int _w, int _ansx, int _ansy) : \
    num(_n), vertex(_v), weight(_w), ansx(_ansx), ansy(_ansy) {}
    bool operator < (const Node &other) const {
        return num < other.num;
    }
};

const int N = 100005;
vector<Node>v[N], vv;
// first元素是询问的编号,pair的第一个元素是询问的 
int ch[N][2], w[N];
int n, m;
int num[N<<1], cnt;
map<int,int>mp;
int lbit[N<<1], rbit[N<<1]; // 因为询问和给定节点加起来上限是2*N个不同的数值 

inline int lowbit(int x) {
    return x & -x;
}

void add(int bit[], int x, int val) {
    for (int i = x; i <= cnt; i += lowbit(i)) {
        bit[i] += val;
    }
}

int sum(int bit[], int x) {
    int ret = 0;
    for (int i = x; i > 0; i -= lowbit(i)) {
        ret += bit[i];
    }
    return ret;
}

void dfs(int u) {
    for (int i = 0; i < (int)v[u].size(); ++i) {
        int weight = mp[v[u][i].weight];
        int lsum = sum(lbit, weight), rsum = sum(rbit, weight);
        int ltot = sum(lbit, cnt), rtot = sum(rbit, cnt);
        bool find = lsum && bool(lsum - sum(lbit, weight-1)) || rsum && bool(rsum - sum(rbit, weight-1));
        if (!find) {
            v[u][i].ansx = rsum;
            v[u][i].ansy = rtot-rsum + ltot-lsum + lsum*3 + rsum*3;
        } else {
            v[u][i].ansx = v[u][i].ansy = -1;
        }
    }
    if (ch[u][0]) {
        add(lbit, mp[w[u]], 1);
        dfs(ch[u][0]);
        add(lbit, mp[w[u]], -1);
    }
    if (ch[u][1]) {
        add(rbit, mp[w[u]], 1);
        dfs(ch[u][1]);
        add(rbit, mp[w[u]], -1);
    }
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        cnt = 0, mp.clear(), vv.clear();
        memset(ch, 0, sizeof (ch));
        memset(lbit, 0, sizeof (lbit));
        memset(rbit, 0, sizeof (rbit));
        for (int i = 1; i <= n; ++i) {
            v[i].clear();
            scanf("%d", &w[i]);
            num[cnt++] = w[i]; // 将所有的要进行处理的节点重量以及询问的重量离散化 
        }
        scanf("%d", &m);
        int a, b, c;
        for (int i = 0; i < m; ++i) {
            scanf("%d %d %d", &a, &b, &c);
            ch[a][0] = b, ch[a][1] = c;
        }
        int Q;
        scanf("%d", &Q);
        for (int i = 0; i < Q; ++i) {
            scanf("%d %d", &a, &b);
            v[a].push_back(Node(i, a, b, 0, 0));
            num[cnt++] = b;
        }
        sort(num, num + cnt);
        cnt = unique(num, num + cnt) - num; // 离散化之后一共是cnt个元素 
        for (int i = 0; i < cnt; ++i) {
            mp[num[i]] = i + 1; // 这里加1是为了避免树状数组统计时无法处理0号元素 
        }
        dfs(1); // 题目中约定了1为根
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < (int)v[i].size(); ++j) {
                vv.push_back(v[i][j]);
            }
        }
        sort(vv.begin(), vv.end());
        for (int i = 0; i < (int)vv.size(); ++i) {
            printf(vv[i].ansx == -1 ? "0\n" : "%d %d\n", vv[i].ansx, vv[i].ansy);
        }
    }
    return 0;
}
View Code

相关文章: