hdu-4605 Magic Ball Game[离散化+回溯+树状数组]

题目大意很简单。

有一颗树(10^5结点),所有结点要么没有子结点,要么有两个子结点。
然后每个结点都有一个重量值,根结点是1

然后有一个球,从结点1开始往子孙结点走。

每碰到一个结点,有三种情况

如果此球重量等于该结点重量,球就停下了

如果此球重量小于该结点重量,则分别往左右儿子走的可能都是1/2

如果此球重量大于该结点重量,则走向左儿子的概率是1/8,右儿子的概率是7/8

然后若干个询问(10^5次),问一个重量为x的球经过结点v的概率


对于从v到根节点(不包括v),假如我们知道 ls (向左走比x小的节点个数)、rs(向右走比x小的节点个数)、lb(向左走比x大的节点个数)、rb(向右走比x大的节点的个数)

那么概率就是 c= (1/2)^ls * (1/8)*lb * (1/2) ^ rs * (7/8)^rb转换成题目要求的输出就是 a=rs,b=3*(rs+ls)+lb+rb;

离线做法:

要求得上述值。可以搜索整颗树,到v点时处理对v点的查询。还需要记录v到root路径上的节点状态,所以要回溯。还需要快速求的从v到根的路径上向左 和 向右 的 重量区间的节点出现个数,就可以用树状数组记录每个重量区间包含多少个节点,因为重量范围很大,所以需要离散化。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=100008;
int n,q,size;
int v[maxn*2];//节点数值
int sub_v[maxn*2];
int dv[maxn*2];//节点离散化后的数值
int lbit[maxn*4];//bit处理向左走的节点值域
int rbit[maxn*4];//bit处理向右走的节点值域
int X[maxn];
int Y[maxn];
vector<int> query[maxn];//节点的查询
vector<int> g[maxn];//树节点
void add_edge(int u,int v)
{
	g[u].push_back(v);
}
void init()
{
	for(int i=0;i<maxn;++i)
	{
		g[i].clear();
		query[i].clear();
	}
}
void init_bit()
{
	memset(lbit,0,sizeof(lbit));
	memset(rbit,0,sizeof(rbit));
}
int lowbit(int x)
{
	return x&(-x);
}
void add(int k,int num,int which)
{
	while(k<=size)
	{
		if(!which) lbit[k]+=num;
		else rbit[k]+=num;
		k+=lowbit(k);
	}
}
int read(int k,int which)
{
	int sum=0;
	while(k)
	{
		if(!which)
			sum+=lbit[k];
		else sum+=rbit[k];
		k-=lowbit(k);
	}
	return sum;
}
void dfs(int u,int lct,int rct)
{
	for(int i=0;i<query[u].size();++i)
	{
		int num=query[u][i],x=dv[n+num];
		// printf("%d\n", read(x,0)-read(x-1,0));
		// printf("%d\n", read(x,1)-read(x-1,1));
		if((read(x,0)-read(x-1,0))||(read(x,1)-read(x-1,1)))
		{
			X[num]=-1;
			continue;
		}
		int ls=read(x,0),rs=read(x,1);
		int lb=read(size,0)-read(x,0),rb=read(size,1)-read(x,1);
		X[num]=rs,Y[num]=3*(rs+ls)+lb+rb;
	}
	if(g[u].size())
	{
		int dt=dv[u];
		//向左移动
		add(dt,1,0),dfs(g[u][0],lct+1,rct),add(dt,-1,0);
		//向右移动
		add(dt,1,1),dfs(g[u][1],lct,rct+1),add(dt,-1,1);
	}
}
void discretize()
{
	int _n=n+q;
	for(int i=1;i<=_n;++i)
		sub_v[i]=v[i];
	sort(sub_v+1,sub_v+_n+1);
	size=unique(sub_v+1,sub_v+_n+1)-sub_v-1;
	for(int i=1;i<=_n;++i)
		dv[i]=lower_bound(sub_v+1,sub_v+size+1,v[i])-sub_v;
}
void solve()
{
	init_bit();
	discretize();
	dfs(1,0,0);
	for(int i=1;i<=q;++i)
	{
		if(X[i]==-1)
			printf("0\n");
		else printf("%d %d\n",X[i],Y[i]);
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		init();
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
			scanf("%d",v+i);
		int m,u,a,b;
		scanf("%d",&m);
		for(int i=0;i<m;++i)
		{
			scanf("%d%d%d",&u,&a,&b);
			add_edge(u,a),add_edge(u,b);
		}
		int x,vn;
		scanf("%d",&q);
		for(int i=1;i<=q;++i)
		{
			scanf("%d%d",&vn,&x);
			v[n+i]=x;
			query[vn].push_back(i);
		}
		solve();
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值