bzoj 2784 时间流逝 —— 树上高斯消元

时间:2023-03-09 17:28:50
bzoj 2784 时间流逝 —— 树上高斯消元

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2784

其实转移是一棵树,从根到一个点表示一种能量圈状态,当能量值大于 T 是停止,也就是成为叶子;

点数大约是整数划分,据说是 1.2e6 左右,可以 dfs;

设 \( d[x] \) 是儿子数,则 \( f[x] = p*(f[fa]+1) + (1-p) \frac{\sum\limits_{v \in son}(f[v]+1)}{d[x]} \)

仍然设 \( f[x] = K[x] * f[fa] + B[x] \),得到 \( K[x] = \frac{d[x]*p}{d[x]-(1-p) \sum A[v] } , B[x] = \frac{(1-p)\sum B[v] + d[x]}{d[x]-(1-p) \sum A[v]} \)

走到叶子停止,而叶子的值本来就是 0,所以直接做即可;

注意根的 \( p \) 不同,直接在根处把 \( p \) 改成 0 即可。

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef double db;
int rd()
{
int ret=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=; ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return f?ret:-ret;
}
int T,n,w[]; db p;
struct N{
db K,B;
N(db k=,db b=):K(k),B(b) {}
};
N dfs(int sum,int d)
{
if(sum>T)return N(,);
db ks=,bs=,P=(sum==?:p);
for(int i=;i<=d;i++)
{
N tmp=dfs(sum+w[i],i);
ks+=tmp.K; bs+=tmp.B;
}
return N(d*P/(d-(-P)*ks),((-P)*bs+d)/(d-(-P)*ks));
}
int main()
{
while(~scanf("%lf",&p))
{
T=rd(); n=rd();
for(int i=;i<=n;i++)w[i]=rd();
sort(w+,w+n+);
printf("%.3f\n",dfs(,n).B);
}
return ;
}