2013 多校第一场 hdu 4605 Magic Ball Game

hdu 4605

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4605

题目大意:给你一棵二叉树,每个节点有一个w值,现在有一颗小球,值为x,从根节点往下掉,如果w==x,那么它就会停止;如果w>x,那么它往左、右儿子的概率都是1、2;如果w<x,那么它往左儿子的概率是1/8,右儿子是7/8。现在给你q个询问,问你值为x的球道达节点u的概率为多少。

思路:比赛的时候,我们都是跟着榜走的,都卡在那道Deque上了,根本没有看这题。昨天晚上自己看了一下,其实也没有什么正确的解题思路,暴力谁都会,但是肯定超时,要不然也不会比赛时没几个队伍去做这题了。然后,当然就看题解了喽,看题解先开始就是有一个地方一直不明白,它所说的树状数组(线段树)到底维护的是什么,它的区间又是什么东西。想来想去一直没想明白,去网上看别人的博客,东看西看又找了个同学去问,感觉也还是这几个地方不懂(当然不是他们讲的不好,而是我有个地方卡壳了,对线段树这个工具应用还不熟啊),后来,自己按照他们讲的方向去想了想,终于顿悟了。它不是每次询问都输出,而是先把所有的询问都存起来,遍历的时候一次性弄完,再输出,这个不是重点,关键还是在我上面所说的树状数组那里,对于树状数组来说,每个叶子节点A[ i ]存的是当前dfs 走过的路径上 w为 i 的结点个数,这样dfs下来的时候,每次都做update操作(递归时A[ w ]+1,回溯时再减掉),当当前节点被mark时,就询问。关于x、y的计算方法是这样的:设a为从根节点到当前节点左路径中小于x的节点数,b为从根节点到当前节点左路径中大于x的节点数,c为右路径中小于的,d为右路径中大于的,则 x = c,y = b+d+3*(a +c),这个自己画个图想想,列列式子就明白了。由于dfs到某个节点,对应的树状数组里存的就是这条路径对应的个数,询问的复杂度是O(logn)。多以整个过程在一遍dfs过后就完成了。由于分左、右路径,所以我就开了两个树状数组,一个表示左,一个表示右,那些开一个的其实也一样,都要分左、右的。

话说,这道题之前我只会线段树,树状数组不会,虽然说是一直都想学来着。。 = = ,因为这道题,网上大家写的都是树状数组,我也想看看树状数组到底是什么东西,就去学了。结果发现,这东西果然简单,学学就是5分钟的事情,然后再顺便A道题目。。 它能实现一些简单的求和和更新操作,虽然适用范围比线段树小,但是人家编程复杂度小啊,敲起来快,简单,空间也省。因为这道题只需要求和和单点更新,果断是树状数组啊!

今天早上来了,果断把它A了,可是我这个代码我发现一个神奇的问题,如果我在dfs中处理mark节点时如果sum(x),sum(x-1)这些值先算好保存下来(免得重复算么)再算要求的值,就是RE(爆栈),而直接当场就算,就是AC的。。 神奇啊,不解。。 = = 

代码如下:

#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;

#define MP make_pair

const int MAXN = 111111 ;

struct Node
{
    int w,lson,rson;
} node[MAXN];

int mark[MAXN];
vector < pair <int,int> > ques[MAXN];

int ans[MAXN][2];

int tot;

int sum_l[MAXN],sum_r[MAXN];

map <int,int> my_map;

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

void update_left(int pos,int x)
{
    while(pos<=tot)
    {
        sum_l[pos]+=x;
        pos += lowbit(pos);
    }
}

void update_right(int pos,int x)
{
    while(pos<=tot)
    {
        sum_r[pos]+=x;
        pos += lowbit(pos);
    }
}

int sum_left(int pos)
{
    int s=0;
    while(pos>=1)
    {
        s += sum_l[pos];
        pos -= lowbit(pos);
    }
    return s;
}

int sum_right(int pos)
{
    int s=0;
    while(pos>=1)
    {
        s += sum_r[pos];
        pos -= lowbit(pos);
    }
    return s;
}

void dfs(int root)
{
    if(root==-1) return ;
    if(mark[root])
    {
        for(int i = 0;i<ques[root].size();i++)
        {
            int x  = my_map[ques[root][i].first];
            int id = ques[root][i].second;
            if(sum_left(x)-sum_left(x-1)>0||sum_right(x)-sum_right(x-1)>0) ans[id][0] = -1;
            else
            {
                ans[id][0] = sum_right(x-1);
                ans[id][1] = sum_left(tot)-sum_left(x)+sum_right(tot)-sum_right(x)+3*(sum_left(x-1)+sum_right(x-1));
            }
        }
    }
    update_left(my_map[node[root].w],1);
    dfs(node[root].lson);
    update_left(my_map[node[root].w],-1);
    update_right(my_map[node[root].w],1);
    dfs(node[root].rson);
    update_right(my_map[node[root].w],-1);
}

void init()
{
    memset(sum_l,0,sizeof(sum_l));
    memset(sum_r,0,sizeof(sum_r));
}

int hash[MAXN];

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&node[i].w);
            hash[i] = node[i].w;
        }
        int m;
        scanf("%d",&m);
        int a,b,c;
        for(int i =1;i<=n;i++)
            node[i].lson = node[i].rson = -1;
        while(m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            node[a].lson = b;
            node[a].rson = c;
        }
        int q;
        scanf("%d",&q);
        memset(mark,0,sizeof(mark));
        for(int i = 1;i<=n;i++)
            ques[i].clear();
        int u,x;
        for(int i = 1;i<=q;i++)
        {
            scanf("%d%d",&u,&x);
            mark[u] =1;
            ques[u].push_back(MP(x,i));
            hash[n+i] = x;
        }
        sort(hash+1,hash+1+n+q);
        tot = unique(hash+1,hash+1+n+q)-hash-1;
        //printf("tot = %d\n",tot);
        my_map.clear();
        for(int i=1;i<=tot;i++)
            my_map[hash[i]] = i;
        init();
        dfs(1);
        for(int i = 1;i<=q;i++)
        {
            if(ans[i][0]==-1) puts("0");
            else printf("%d %d\n",ans[i][0],ans[i][1]);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值