HDU 4605 Magic Ball Game 解题报告

题目:

一棵n个节点的二叉树,每个非叶子节点都有左右儿子,每个节点都有一个权值w[u],从根丢一个值X的球,沿着树链往下走:

如果w[u]=X或者到了叶子,球就停下来

如果w[u]<X,球有1/8的概率往左边走,有7/8的概率往右边走

如果w[u]>X,球有1/2的概率往左边走,有1/2的概率往右边走

询问(v,x),当X=x时,有多少概率经过点v。

解法:

问题抽象成,求树根到点v的链上,有多少个点权值比x大,且往左孩子走(×1/2),有所少个点权值比x小,且往右孩子走(×1/2),有多少个点权值比x小,且往左孩子走(×1/8),有多少个点权值比x小,且往右孩子走(×7/8)。

考虑将所有权值离散化,离线的处理每一个询问。

利用两个树状数组分别表示往左变走的节点权值出现的次数,往右边走的节点权值次数。

dfs整棵树,若当前节点为u,先处理关于u的询问,如果x在根到u之前出现过了,那么概率为0,

否则,利用树状数组计算从根到u的四种点的个数,然后根据概率的分子分母的概率求得答案,

枚举左孩子,并将点u加入表示左边的树状数组中,遍历完左子树之后,再将点u从左边的树状数组中删掉,右孩子同理。

代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100005

int n,m,qq;
int tree[maxn][2];
int ind[maxn<<1],cnt,c[2][maxn],ans[maxn][2],w[maxn];
bool vis[maxn];
struct node
{
    int x,next,id;
}Q[maxn];
int qcnt,pre[maxn];
void addQ(int u,int x,int id)
{
    Q[qcnt].x=x;
    Q[qcnt].next=pre[u];
    Q[qcnt].id=id;
    pre[u]=qcnt++;
}
int bs(int key)
{
    int l=1,r=cnt;
    while (l<=r)
    {
        int mid=(l+r)>>1;
        if (ind[mid]==key) return mid;
        if (ind[mid]<key) l=mid+1;
        else r=mid-1;
    }
    return -1;
}
int lowbit(int x)
{
    return x&(-x);
}
void insert(int p,int x,int add)
{
    for (;x<=cnt;x+=lowbit(x)) c[p][x]+=add;
}
int cal(int p,int x)
{
    int res=0;
    for (;x;x-=lowbit(x)) res+=c[p][x];
    return res;
}
void dfs(int u,int dep)
{
    for (int i=pre[u];i!=-1;i=Q[i].next)
    {
        int x=bs(Q[i].x);
        if (vis[x])
        {
            ans[Q[i].id][0]=-1;
            continue;
        }
        int tmp1=cal(0,x-1),tmp2=cal(0,cnt)-tmp1;
        int tmp3=cal(1,x-1),tmp4=cal(1,cnt)-tmp3;
        ans[Q[i].id][0]=tmp3;
        ans[Q[i].id][1]=tmp1*3+tmp2+tmp3*3+tmp4;
    }
    if (tree[u][0])
    {
        vis[w[u]]=1;
        insert(0,w[u],1);
        dfs(tree[u][0],dep+1);
        insert(0,w[u],-1);
        insert(1,w[u],1);
        dfs(tree[u][1],dep+1);
        insert(1,w[u],-1);
        vis[w[u]]=0;
    }
}
int main()
{
    int cas,u,a,b;
    scanf("%d",&cas);
    while (cas--)
    {
        qcnt=0;
        memset(tree,0,sizeof(tree));
        memset(pre,-1,sizeof(pre));
        memset(vis,0,sizeof(vis));
        memset(c,0,sizeof(c));
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            scanf("%d",&w[i]);
            ind[i]=w[i];
        }
        scanf("%d",&m);
        for (int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&a,&b);
            tree[u][0]=a;
            tree[u][1]=b;
        }
        scanf("%d",&qq);
        for (int i=1;i<=qq;i++)
        {
            scanf("%d%d",&a,&b);
            addQ(a,b,i);
            ind[n+i]=b;
        }
        sort(ind+1,ind+n+qq+1);
        cnt=unique(ind+1,ind+n+qq+1)-ind-1;
        for (int i=1;i<=n;i++)
            w[i]=bs(w[i]);
        dfs(1,1);
        for (int i=1;i<=qq;i++)
            if (ans[i][0]==-1)
                printf("0\n");
            else printf("%d %d\n",ans[i][0],ans[i][1]);
    }
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值