卧薪尝胆,厚积薄发。
JLOI2012 时间流逝
Date: Tue Apr 02 22:23:35 CST 2019 In Category: NoCategory

Description:

有 $N$ 种能量圈,每天有 $1-p$ 的概率等概率的得到一个能量圈, $P$ 的概率失去一个能量圈,问期望多少天后能量圈的能量总和达到 $T$ 。
$1\leqslant N,T\leqslant 50$

Solution:

因为 $T$ 最大只有 $50$ ,因此状态数实际上是非常少的,首先我们可以暴力 $DP$ : $$ f[i]=1+p\times f[last[i]]+(1-p)\times \frac1{|nxt[i]|}\times\sum_jf[nxt[i][j]] $$ 由于每个点只有一个 $last[i]$ ,所以转移形成了一棵树。
然后有一个套路叫做树上消元,也就是上面那个 $DP$ 式子我们可以高斯消元,但是由于是一棵树我们可以用另一些做法来优化复杂度:
考虑把 $f[i]$ 表示成 $f[i]=K_if[last[i]]+B_i$ 的形式,先假装他成立: $$ \begin{align} f[i]&=1+p\times f[last[i]]+(1-p)\times \frac1{|nxt[i]|}\times \sum_jK_{nxt[i][j]}f[i]+(1-p)\times \frac1{|nxt[i]|}\times \sum_jB_{nxt[i][j]}\\ 设&A_i=(1-p)\times \frac1{|nxt[i]|}\\ (1-&A_i\times \sum_jK_{nxt[i][j]})f[i]=1+p\times f[last[i]]+A_i\times \sum_jB_{nxt[i][j]}\\ f[i]&=\frac p{1-A_i\times \sum_jK_{nxt[i][j]}}\times f[last[i]]+\frac{1+A_i\times \sum_jB_{nxt[i][j]}}{1-A_i\times \sum_jK_{nxt[i][j]}}\\ \end{align} $$ 即: $$ K_i=\frac p{1-A_i\times \sum_jK_{nxt[i][j]}},B_i=\frac{1+A_i\times \sum_jB_{nxt[i][j]}}{1-A_i\times \sum_jK_{nxt[i][j]}} $$ 于是直接求一遍就行了,最后根节点的 $B$ 就是答案。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int n,t;
#define MAXN 51
double p;
int a[MAXN];
struct state
{
double k,b;
};
state dfs(int sum,int minv)
{
if(sum > t)return (state){0,0};
double sumk = 0,sumb = 0;
for(int i = 1;i <= minv;++i)
{
state nxt = dfs(sum + a[i],i);
sumk += nxt.k;sumb += nxt.b;
}
double P = sum ? p : 0;
double A = (1 - P) / minv;
sumk = 1 - A * sumk;sumb = 1 + A * sumb;
double k = P / sumk,b = sumb / sumk;
return (state){k,b};
}
int main()
{
while(~scanf("%lf%d%d",&p,&t,&n))
{
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
sort(a + 1,a + 1 + n);
printf("%.3lf\n",dfs(0,n).b);
}
return 0;
}
In tag: DP-树上消元
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡