一、题目
二、解法
首先考虑一个朴素
d
p
dp
dp,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为第
i
i
i个点的树深度不大于
j
j
j的概率(方便转移,用的时候减一下就行了),转移就考虑当前这条边断不断裂,转移方程如下:
d
p
[
u
]
[
i
]
=
∏
d
p
[
v
]
[
i
−
1
]
+
1
2
dp[u][i]=\prod\frac{dp[v][i-1]+1}{2}
dp[u][i]=∏2dp[v][i−1]+1这样做的话时间复杂度
O
(
q
3
)
O(q^3)
O(q3),观察到本题不需要取模,且允许精度误差,而且一个点对它祖先的影响是指数级下降的,所以我们定一个值
x
x
x,表示树高超过
x
x
x我们就不再考虑它的影响,这样
d
p
dp
dp树组和转移的复杂度就会大大降低,时间复杂度
O
(
q
x
2
)
O(qx^2)
O(qx2),对于此题我们把
x
x
x定为
50
50
50足矣,贴个代码。
#include <cstdio>
#define db double
const int N = 50;
const int M = 500005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,t,fa[M],q[M];db dp[M][N];
void add(int x,int y)
{
t=0;
for(int i=0;i<N;i++)
dp[x][i]=1;
for(int i=1,k=y;i<N && k;i++,k=fa[k])
q[++t]=k;
for(int i=t;i>1;i--)
{
int u=q[i],v=q[i-1];
for(int j=1;j<N;j++)
dp[u][j]/=0.5*(dp[v][j-1]+1);
}
dp[y][0]*=0.5;
for(int i=1;i<t;i++)
{
int u=q[i+1],v=q[i];
for(int j=1;j<N;j++)
dp[u][j]*=0.5*(dp[v][j-1]+1);
}
}
db ask(int x)
{
db r=0;
for(int i=1;i<N;i++)
r+=(dp[x][i]-dp[x][i-1])*i;
return r;
}
signed main()
{
n=read();
m++;
for(int i=0;i<N;i++)
dp[m][i]=1;
while(n--)
{
int op=read(),x=read();
if(op==1)
{
m++;
add(m,fa[m]=x);
}
if(op==2)
printf("%.10lf\n",ask(x));
}
}