数据结构--离线树状数组--hdu4605 Magic Ball Game

n个点的树,每个点有一个权值。1为根,每个点要么有2个儿子,要么没有儿子。

Q次询问,每次给节点v,和数X,一个球从根往下走:

1.X = w[i]或i没有儿子,球停下

2.X < w[i],球有1/2可能往左走,1/2可能右走

3.X > W[I],球有1/8可能往左走,7/8可能右走

统计根到v的路径上,(往左走、往右走)*(大于x、小于x)的个数

则到达v点的可能性为(1/2)^(左&小于) * (1/2)^(左&大于) * (1/8)^(左&小于) * (7/8)^(左&大于)

//如果路径上有x == w[i]则之后可能性都为0

在dfs时用树状数组统计,每个时候树状数组中统计的东西,就是根到v上节点的信息。


#include <cstdio>
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 5;

int T,N,M,Q;
int u,a,b;
struct node{
    int l = -1,r = -1;
}ns[maxn];

int opv[maxn];
struct num{
    int x,id;
}opx[maxn],w[maxn];

map<int,int> id;
const int maxtr = maxn << 1;
int trl[maxtr + 10],trr[maxtr + 10];//记录wi出现的个数
vector<int> mpv[maxn];
int cnt7[maxn],cnt2[maxn];//cnt7[]作为分母,为-1表示答案为0,>=0表示7的指数
int p;

void add(int tr[],int x,int d)
{
    while(x < maxtr){
        tr[x] += d;
        x += (x & -x);
    }
}
int query(int tr[],int x)
{
  //  printf("query x = %d\n",x);
    int ans = 0;
    while(x){
        ans += tr[x];
        x -= (x & -x);
    }
    return ans;
}
void dfs(int s)
{

    for(int i = 0;i < mpv[s].size();i ++){
        int opid = mpv[s][i];//离散化后的数字
        int x = opx[opid].id;
        int e = query(trl,x) - query(trl,x - 1) + query(trr,x) - query(trr,x - 1);
        if(e) {cnt7[opid] = -1;continue;}//如果之前有等于x,那么一定为0
        int a = query(trl,x - 1);//左儿子w[i] < X的个数
        int b = query(trl,p) - query(trl,x);//左儿子w[i] > X的个数
        int c = query(trr,x - 1);//右儿子w[i] < X的个数
        int d = query(trr,p) - query(trr,x);//右儿子w[i] > X的个数
        cnt7[opid] = c;
        cnt2[opid] = b + d + 3 * (a + c);
       // ans[opid] = (1/2)^b * (1/8)^a * (1/2)^d * (7/8)^c
    }
    
    if(ns[s].l != -1) {
        add(trl,w[s].id,1);dfs(ns[s].l);add(trl,w[s].id,-1);
        add(trr,w[s].id,1);dfs(ns[s].r);add(trr,w[s].id,-1);
    }
}
void init(){
    id.clear();
    for (int i = 1; i <= N; i ++) {
        ns[i].l = ns[i].r = -1;
        mpv[i].clear();
    }
    memset(trl, 0, sizeof(trl));
    memset(trr, 0, sizeof(trr));
}
int main()
{
    scanf("%d",&T);
    while (T --) {
        scanf("%d",&N);init();
        for(int i = 1;i <= N;i ++) {scanf("%d",&w[i].x);id[w[i].x];}
        scanf("%d",&M);
        for(int i = 1;i <= M;i ++){
            scanf("%d%d%d",&u,&a,&b);
            ns[u].l = a;ns[u].r = b;
        }
        scanf("%d",&Q);
        for(int i = 1;i <= Q;i ++){
            scanf("%d%d",&opv[i],&opx[i].x);
            id[opx[i].x];
        }
        p = 1;
        for(auto &a:id) a.second = p ++;
        for(int i = 1;i <= N;i ++) w[i].id = id[w[i].x];
        for(int i = 1;i <= Q;i ++) {
            opx[i].id = id[opx[i].x];
            mpv[opv[i]].push_back(i);
        }
        
        dfs(1);
        for(int i = 1;i <= Q;i ++){
            if(cnt7[i] == -1) printf("0\n");
            else{
                printf("%d %d\n",cnt7[i],cnt2[i]);
            }
        }
    }
   
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值