hdu 4605 Magic Ball Game(离线+树状数组)

题意:有T组测试数据,N表示这棵树有N个点,下一行的N个数,表示每个点的权值W,M表示边的关系,接下来的M行,每行有三个数字,u,b,b,表示点a和点b分别是点u的左儿子和右儿子。Q表示查询数,接下来的Q行,每行两个数字,v和X。

 一个权值为X球从根节点开始下落,每落到一个节点的时候,1.如果X=W[i],或者没有儿子节点了,球停止下落。2.如果X<W[i],球各有1/2的概率落到左右儿子节点。3.如果X>W[i],球有1/8的概率落到左儿子,有7/8的概率落到右儿子。问球落到点v的概率是多少(概率用7^x/2^y表示,即输出x和y就可以)?

以上复制与shiqi_614大牛关于这道题的描述。

做法我研究了许久才弄懂,一开始直接用线段树的结构去从那个点往上走,后来发觉这不是一个完全二叉树。。

做法是把树的每条链用树状数组去维护,DFS这颗树,用一种类似回朔的办法。对于权值离散化,我用的是map映射,走到某个节点,如果要往左边走,就把这个节点权值加1加入第一颗树状数组,往右走就加入第二颗,如果当前节点有询问,直接用lower_bound找到>=x的第一个值如果这个值等于x且sum(pos,0)-sum(pos-1,0)或者sum(pos,1)-sum(pos-1,1)>=1那么就说明这条链上有等于x的,就不可达到。不然就统计前缀和,代表前面有几个小于x的值,以及可以算出后面有几个大于x的值,然后计算既可。

AC代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<ctype.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdlib>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<ctime>
#include<string.h>
#include<string>
#include<bitset>
using namespace std;
#define ll __int64
#define eps 1e-8
#define NMAX 100000+10
template<class T>
inline void scan_d(T &ret)
{
    char c;
    int flag = 0;
    ret=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c == '-')
    {
        flag = 1;
        c = getchar();
    }
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
    if(flag) ret = -ret;
}
int w[NMAX],a[2][NMAX],G[NMAX][2],wei[NMAX],nct,ans[NMAX][2];
map<int,int>mp;
vector<pair<int, int > >v[NMAX];
int lowbit(int x)
{
    return x&(-x);
}

int sum(int x, int flag)
{
    int ret = 0;
    while(x > 0)
    {
        ret += a[flag][x];
        x -= lowbit(x);
    }
    return ret;
}

void add(int x, int d, int flag)
{
    while(x <= nct)
    {
        a[flag][x] += d;
        x += lowbit(x);
    }
}

map<int,int>::iterator it;
void dfs(int pos)
{
    int sz = v[pos].size();
    for(int i = 0; i < sz; i++)
    {
        int x = v[pos][i].second,k = v[pos][i].first,pp;
        it = mp.lower_bound(x);//第一个>=x的
        if(it == mp.end()) pp = nct+1;
        else pp = it->second;
        int t1 = sum(pp,0)-sum(pp-1,0),t2 = sum(pp,1)-sum(pp-1,1);
        if(it->first == x && (t1 >= 1 || t2 >= 1))
        {
            ans[k][0] = -1;
            continue;
        }
        t1 = sum(pp-1,0); t2 = sum(pp-1,1);
        int t3 = sum(nct,0)-t1, t4 = sum(nct,1)-t2;
//        cout<<x<<" "<<pp<<" "<<t1<<" "<<t2<<" "<<t3<<" "<<t4<<endl;
        ans[k][0] = t2;
        ans[k][1] = 3*t1+3*t2+t3+t4;
    }
    if(G[pos][0] == 0) return;
    add(mp[w[pos]],1,0);
    dfs(G[pos][0]);
    add(mp[w[pos]],-1,0);
    add(mp[w[pos]],1,1);
    dfs(G[pos][1]);
    add(mp[w[pos]],-1,1);
}

int main()
{
#ifdef GLQ
    freopen("input.txt","r",stdin);
//    freopen("o4.txt","w",stdout);
#endif // GLQ
    int t,n,m,q,i,j;
    scanf("%d",&t);
    while(t--)
    {
        mp.clear();
        memset(a,0,sizeof(a));
        scanf("%d",&n);
        for(i = 1; i <= n; i++)
            scanf("%d",&w[i]);
        scanf("%d",&m);
        memset(G,0,sizeof(G));
        for(i = 0; i < m; i++)
        {
            int temp;
            scanf("%d",&temp);
            scanf("%d%d",&G[temp][0],&G[temp][1]);
        }
        memcpy(wei,w,sizeof(w));
        sort(wei+1,wei+1+n);
        nct = unique(wei+1,wei+n+1)-wei-1;
        for(i = 1; i <= nct; i++)
            mp[wei[i]] = i;
        scanf("%d",&q);
        for(i = 1; i <= n; i++) v[i].clear();
        for(i = 0; i < q; i++)
        {
            int p,x;
            scanf("%d%d",&p,&x);
            v[p].push_back(make_pair(i,x));
        }
        dfs(1);
        for(i = 0; i < q; i++)
            if(ans[i][0] == -1) printf("0\n");
            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、付费专栏及课程。

余额充值