题目大意很简单。
有一颗树(10^5结点),所有结点要么没有子结点,要么有两个子结点。
然后每个结点都有一个重量值,根结点是1
然后有一个球,从结点1开始往子孙结点走。
每碰到一个结点,有三种情况
如果此球重量等于该结点重量,球就停下了
如果此球重量小于该结点重量,则分别往左右儿子走的可能都是1/2
如果此球重量大于该结点重量,则走向左儿子的概率是1/8,右儿子的概率是7/8
有一颗树(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;
}
探讨一个关于球沿特定权重树形结构下落并计算到达指定节点概率的问题。通过离散化和树状数组技术预处理节点权重,实现高效查询。

被折叠的 条评论
为什么被折叠?



