[JLOI2012]时间流逝

[JLOI2012]时间流逝

题目大意

\(n\)种宝⽯,第\(i\)种的能量是\(a_i\)。开始你没有任何宝⽯。
每⼀天你有\(p\)的概率失去⼀个能量最小的宝⽯,
否则可以在所有能量不超过你拥有的任何⼀个宝⽯的宝⽯中等概率随机获得⼀个。
每种宝石有无限多个。如果某⼀天你没有宝⽯,则不存在概率失去宝⽯。
求第⼀次宝⽯的能量总和超过 \(T\)期望天数
数据范围:\(0\leq T,n\leq 50\) ; \(0.1\leq p\leq 0.9\)

题解

状态数不超过\(P(T)\)是吧。其中\(P(x)\)表示\(x\)的整数拆分数。
发现\(P(50)\)大概是\(5*10^5\)左右。
所以爆搜不虚。我们考虑记录所选的拆分数。
\(E_S\)表示状态为\(S\)时,到目标状态的期望还需天数。
转移:
\[E_S = 1 + pE_{last(S)} + (1-p)\frac{1}{|next(S)|}\sum E_{next(S)}\]
其中\(last(S)\)是去掉最小石头后的前驱状态,\(next(S)\)是获得一个石头后的后继状态。
高斯消元? No...No...No...
你可以发现这貌似是一个树形结构?搞事情?
\(last(S)\)看成\(S\)的父亲,\(next(S)\)看成\(S\)的儿子。
从底往上转移,我们是不是可以把\(E_S\)写成\(kE_{last(S)} + b\)的形式?
\(E_{next(S)}\)拆开:
\[E_S = 1 + pE_{last(p)} +(1-p)\frac{1}{|next(S)|}\sum (kE_S + b)\]
\(G = (1-p)\frac{1}{|next(S)|}\),移项:
\[(1-G\sum k)E_S = 1 + pE_{last(p)} + G\sum b\]
所以:
\[E_S = \frac{p}{1 - G\sum k}E_{last(S)} + \frac{1 + G\sum b}{1 - G\sum k}\]
就是\(E_S = kE_{last(S)} + b\)的形式了。
一路转移到最顶端,初始空状态没有\(E_{last(S)}\),所以它的\(b\)值就是答案了。
注意特判一下初始空状态可以转移到所有状态,此时\(p = 0\)。时间复杂度:\(O(P(T)n)\)

实现代码

#include<bits/stdc++.h>
#define RG register
#define IL inline 
using namespace std;

struct Result{double k,b ; }tmp,ans; int N,T,v[105] ;double P,p ;  

Result dfs(RG int S,RG int mx){
    if(S > T) return (Result){0.0 , 0.0} ; 
    double Next,B = 0,K = 0,sn = 0,r = 1;
    Next = 1.0 / (double)mx ;  
    sn = mx ; 
    for(RG int i = 1; i <= mx; i ++){
        tmp = dfs(S + v[i] , i) ;  
        B = B + tmp.b ;  K = K + tmp.k ;   
    }
    p = S ? P : 0.0 ; 
    r = 1.0 - (1.0 - p) * Next * K ; 
    K = p / r ; B = ((1.0 - p) * Next * B + 1) / r;
    return (Result){K , B} ;    
}

int main(){
    while(scanf("%lf%d%d",&P,&T,&N) != EOF){
        for(RG int i = 1; i <= N; i ++) scanf("%d",&v[i]) ; 
        sort(v + 1 , v + N + 1) ; 
        printf("%.3lf\n" , dfs(0 , N).b) ; 
    }return 0 ; 
}

posted @ 2018-04-25 08:51 GuessYCB 阅读(...) 评论(...) 编辑 收藏