Hdu4605_Magic Ball Game(DFS+树状数组+离散化+离线处理)

题意:

给你一棵二叉树,每个节点上都有一个值。

然后对于每次询问,对于一个球 (pos , weight),球的目标是到达pos ,球的重量为weight ,球从根节点开始往下走。

如果当前节点的值大于球的重量,那么往两边走的概率都是1/2。

如果当前节点的值小于球的重量,那么球往左边走的概率是1/8,右边走的概率是7/8。

如果两者相等,那么球就会停在该节点上。

最后输出从根节点到pos的概率,用(7 ^ x) / ( 2 ^ y) 表示,最后输出x , y 即可。

思路:DFS + 树状数组

很自然的想法是,每次从根找v,dfs算出概率,这样复杂度是nlogn,会超时。
从根到每个节点的路径是唯一的,那么是需要统计出向左比x大的,向左比x小的,向右比x大的,向右比x小的。设这条路径向左孩子节点走的节点的权值比x大的有ld个,比x小的有lx个,向右孩子节点走的节点的权值比x大的有rd个,比x小的有rx个,概率为(1/2)^ld * (1/8)^lx * (1/2)^rd * (7/8)^rx。
得 x= rd,y = ld + rd+ (lx + rx) * 3。统计个数用树状数组。注意当路径中有=x的节点时,这个节点永远无法到达。dfs保证了每次处理的都是一条从根出发的路径,不受孩子节点的影响。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5+5;
using namespace std;
int n, m, q;
int LC[maxn], RC[maxn];		// leftchild, rightchild
int w[maxn], tmp[maxn*2], ansx[maxn], ansy[maxn], C[2][maxn];
vector<int> v1[maxn], v2[maxn];

inline int lowbit(int x){return x & -x;}
void add(int u, int x, int v){for(int i = x; i < maxn; i+= lowbit(i)) C[u][i]+= v;}
int sum(int u, int x){
	int s = 0;
	for(int i = x; i > 0; i-= lowbit(i)) s+= C[u][i];
	return s;
}

void dfs(int u){
	for(int i = 0; i < v1[u].size(); ++i){
		int a = v1[u][i], x = v2[u][i];
		int ltot = sum(0, maxn-1);			// 左边总共
		int rtot = sum(1, maxn-1); 
		int lx = sum(0, x-1);				// 左边比x小的个数
		int rx = sum(1, x-1);
		int ld = sum(0, maxn-1) - sum(0, x);		// 左边比x大的个数
		int rd = sum(1, maxn-1) - sum(1, x);		
		if(ld + lx != ltot||rd + rx != rtot){
			ansx[a] = -1; ansy[a] = -1;
			continue;
		}
		ansx[a] = rx;
		ansy[a] = ld + rd + 3*(lx + rx);
	}
	if(LC[u] != -1){		// 按照题意,有左孩子就一定有右孩子 
		add(0, w[u], 1);
		dfs(LC[u]);
		add(0, w[u], -1);
		add(1, w[u], 1);
		dfs(RC[u]);
		add(1, w[u], -1);
	}
}

int main()
{
    //freopen("in.txt","r",stdin);
    int T; scanf("%d",&T);
	while(T--){
		memset(LC, -1, sizeof(LC));
		memset(C, 0, sizeof(C));
		scanf("%d",&n);
		for(int i = 1; i <= n; ++i){
			scanf("%d",&w[i]); 
			tmp[i] = w[i];
			v1[i].clear(); v2[i].clear();
		}
		scanf("%d",&m);
		for(int i = 0; i < m; ++i){
			int u,a,b; scanf("%d%d%d",&u,&a,&b);
			LC[u] = a;
			RC[u] = b;
		}
		int cnt = n;
		scanf("%d",&q);
		for(int i = 0; i < q; ++i){
			int v,x; scanf("%d%d",&v,&x);
			v1[v].push_back(i);
			v2[v].push_back(x);
			tmp[++cnt] = x;
		}
		
		// 数据范围到1e9,所以要离散化
		sort(tmp+1, tmp+cnt+1);
		int n2 = unique(tmp+1, tmp+cnt+1) - tmp - 1;		// 去重
		for(int i = 1; i <= n; ++i){			// 按照大小重新分配权值,即离散化
			w[i] = lower_bound(tmp+1, tmp+n2+1, w[i]) - tmp;		
			for(int j = 0; j < v2[i].size(); ++j) v2[i][j] = lower_bound(tmp+1, tmp+n2+1, v2[i][j]) - tmp;
		}
		
		dfs(1);
		
		for(int i = 0; i < q; ++i){
			if(ansx[i] == -1) printf("0\n");
			else printf("%d %d\n", ansx[i],ansy[i]);
		}
	}
    fclose(stdin);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值