BZOJ2784[JLOI2012]时间流逝

Description

生活可以很简单。可以探索水底世界的神秘,也可以去发现奇特新生物,亦或踏上一段新生的旅程。在必须要迎接挑战或跟周围的生物进行生存争夺之前,享受自由的飞翔。此时你会觉得生活是如此美好。

像蛇喜欢吃浮游生物一样(哦,我好像忘记告诉你这个常识),每天,你可以吃一些你周围的基础生物,然后会在你的尾巴上得到一个能量圈。你将会有好多种不同的能量圈,每一个都会被赋予一个能量。你可以拥有多个同种的能量圈,但是对于新得到的相同的能量圈,它的能量不能大于你已拥有的任何一个能量圈。除了前面的规则,获得新的能量圈的种类的概率是一样的。一天天过去,你得到越来越多的能量,开始了进化演变。

但是你也有自己的问题,有时你会面对邪恶的果冻鱼。它会追着你咬你,你不得不扔出最小能量值的能量圈然后赶忙逃跑。在这种情况下,你不会有任何的胃口了,因此这天你将不再得到任何能量圈。幸好,当你没有任何能量圈的时候,果冻鱼就算看见你也不会追着你,此时你可以好好地享用美食。

你听说当你的总的能量值超过了某个阈值之后,可以进化成强大模式并能够吃掉果冻鱼。是时候反击了!下面是本题的问题:预计要过多少天你才能进化成强大模式?(第一天默认你没有任何能量圈)

Input

输入包含多个测例。对每个测例会有两行。第一行是一个浮点数P,一个整数T和一个整数N。P是每天遇到果冻鱼的概率,T是阈值。第二行是N个不同的正整数,表示每一种能量圈的能量值。

Output

对于每个测例,输出一行表示预计要过多少天你的能量值能够超过阈值,四舍五入到三位小数。

Sample Input

0.5 0 1

1

0.5 1 2

1 2

Sample Output

1.000

2.000

HINT

对于所有数据,0.1≤P≤0.9,1≤T≤50,1≤N≤30。

题解:

概率DP题,我想了好久以后开始写,调了好几个小时都没有写对。

直到我看了czt的代码,我在发现我原本推出的状态转移方式是错误的。

正确的DP方式:用[i,j]记录已经筹集了i的能量,且拥有的最小能量圈为j,让其发展下去,直到筹集t以上的能量或失去j能量圈为止。

用f[i,j,1]记录有多大的几率是以失去j能量圈结束的,用f[i,j,2]记录结束的期望天数(结束包括以上两种方式)。

初始状态:f[i,j,1]=0,f[i,j,2]=1。(i>t)

假设[i,j]时获得了一个k的能量圈(k<=j),则f[i,j]可以通过f[i+k,k]转移而来。

设x[k]为当前获得k的概率。

没有原地踏步的概率p2为(1-∑(f[i+k,k,2]*x[k]))。

不计原地踏步的情况(即获得k后进入[i+k,k]转态却以失去k结束),可以得出转到[i,k]状态的期望步数和tot为∑((f[i+k,k,2]+1)*x[k])。

推一推公式,可得f[i,j,1]=p/p2,f[i,j,2]=(tot/p2)+f[i,j,1]*1。

对于没有能量圈的状态(即答案),不会遇到鱼,转移方式与上面的略有不同,但大致是一样的。

代码:

 1 var
 2   i,j,k,l,n,m,t:longint;
 3   p1,p2:extended;
 4   b:array[0..1000,0..100,1..2]of extended;
 5   lb,qz:array[0..100]of longint;
 6   ans:extended;
 7 begin
 8   while not eof do
 9   begin
10     fillchar(lb,sizeof(lb),0);
11     fillchar(qz,sizeof(qz),0);
12     fillchar(b,sizeof(b),0);
13     readln(p1,t,n); inc(t); p1:=1-p1;
14     for i:=1 to n do begin read(j); inc(lb[j]); end; readln;
15     for i:=1 to 100 do qz[i]:=qz[i-1]+lb[i];
16     for i:=t-1 downto 1 do
17     for j:=1 to 100 do
18     if lb[j]>0 then
19     begin
20       p2:=1;
21       for k:=1 to j do
22       if lb[k]>0 then
23       begin
24         if i+k<t then
25         begin
26           p2:=p2-p1*(lb[k]/qz[j])*b[i+k,k,1];
27           b[i,j,2]:=b[i,j,2]+p1*(lb[k]/qz[j])*(1+b[i+k,k,2]);
28         end else b[i,j,2]:=b[i,j,2]+p1*(lb[k]/qz[j]);
29       end;
30       b[i,j,1]:=(1-p1)/p2; b[i,j,2]:=b[i,j,2]/p2;
31       b[i,j,2]:=b[i,j,2]+b[i,j,1];
32     end;
33     p2:=1; ans:=0;
34     for k:=1 to 100 do
35     if lb[k]>0 then
36     begin
37       if k<t then
38       begin
39         p2:=p2-(lb[k]/n)*b[k,k,1];
40         ans:=ans+(lb[k]/n)*(1+b[k,k,2]);
41       end else ans:=ans+lb[k]/n;
42     end;
43     writeln((ans/p2):0:3);
44   end;
45 end.

时间: 2024-08-08 09:37:36

BZOJ2784[JLOI2012]时间流逝的相关文章

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

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2784 一个状态可以加很多个能量圈,但减少能量圈的情况只有一种.所以可以用树来刻画. 然后就变成树上高斯消元的套路了.注意根节点的 P 等于 0 . 发现不是要求 dp[ 1 ] 就必须在那个式子里设出 a*dp[ 1 ] 之类的. 据说树上的点大概有 1.2*106 个.大概就是贝尔数吧. #include<cstdio> #include<cstring> #include

洛谷 P3252 [JLOI2012]树

P3252 [JLOI2012]树 题目描述 在这个问题中,给定一个值S和一棵树.在树的每个节点有一个正整数,问有多少条路径的节点总和达到S.路径中节点的深度必须是升序的.假设节点1是根节点,根的深度是0,它的儿子节点的深度为1.路径不必一定从根节点开始. 输入输出格式 输入格式: 第一行是两个整数N和S,其中N是树的节点数. 第二行是N个正整数,第i个整数表示节点i的正整数. 接下来的N-1行每行是2个整数x和y,表示y是x的儿子. 输出格式: 输出路径节点总和为S的路径数量. 输入输出样例

【BZOJ2783】[JLOI2012]树 DFS+栈+队列

[BZOJ2783][JLOI2012]树 Description 在这个问题中,给定一个值S和一棵树.在树的每个节点有一个正整数,问有多少条路径的节点总和达到S.路径中节点的深度必须是升序的.假设节点1是根节点,根的深度是0,它的儿子节点的深度为1.路径不必一定从根节点开始. Input 第一行是两个整数N和S,其中N是树的节点数. 第二行是N个正整数,第i个整数表示节点i的正整数. 接下来的N-1行每行是2个整数x和y,表示y是x的儿子. Output 输出路径节点总和为S的路径数量. Sa

bzoj2783: [JLOI2012]树

2783: [JLOI2012]树 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 753  Solved: 447[Submit][Status][Discuss] Description 数列 提交文件:sequence.pas/c/cpp 输入文件:sequence.in 输出文件:sequence.out 问题描述: 把一个正整数分成一列连续的正整数之和.这个数列必须包含至少两个正整数.你需要求出这个数列的最小长度.如果这个数列不存在则输出-

BZOJ2783: [JLOI2012]树 dfs+set

2783: [JLOI2012]树 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 588  Solved: 347 Description 数列 提交文件:sequence.pas/c/cpp 输入文件:sequence.in 输出文件:sequence.out 问题描述: 把一个正整数分成一列连续的正整数之和.这个数列必须包含至少两个正整数.你需要求出这个数列的最小长度.如果这个数列不存在则输出-1. 输入格式: 每行包含一个正整数n. 每个文件

Boost中的Timer的使用——计算时间流逝

使用Boost中的Timer库计算程序的执行时间 程序开发者都会面临一个共同的问题,即写出高质量的代码完成特定的功能.评价代码质量的一个重要标准就是算法的执行效率,也就是算法的执行时间.为了可靠的提高程序的执行效率,首先要知道执行程序所消耗的时间,然后找出可行的方案对程序进行优化.C++程序员在开发代码的过程中难免会遇见此类问题,本文以Boost中的Timer库为例,详细讲解如何测量程序的执行时间. Boost中Timer库的介绍 Timer是Boost中的一个很小的时间库,提供时间度量和进度显

bzoj2783【JLOI2012】树

2783: [JLOI2012]树 Time Limit: 1 Sec  Memory Limit: 128 MB Submit: 668  Solved: 389 [Submit][Status][Discuss] Description 数列 提交文件:sequence.pas/c/cpp 输入文件:sequence.in 输出文件:sequence.out 问题描述: 把一个正整数分成一列连续的正整数之和.这个数列必须包含至少两个正整数.你需要求出这个数列的最小长度.如果这个数列不存在则输

Broadcast之系统时间流逝广播

广播相关的基础知识在这里我就不详述了,这里我主要是总结下在使用广播时接收不到广播的几种原因.当然,这里只是我个人学习和使用广播的一些个人经验,可能存在不准确或不全的地方,希望看到这篇播客的朋友能够指正.补充,让后面学习的人能够少走弯路,更好更快的学习. 一个广播包含三部分:注册.发送.接收.当你这三部分都包含了,但是还是接收不到广播的话,就很有可能是出现以下问题中的一种或多种. 一.没有添加权限.例如:开机启动的广播,这是一个系统广播,出于安全考虑,系统要求必须声明接收开机启动广播的权限: <u

2783: [JLOI2012]树( dfs + BST )

直接DFS, 然后用set维护一下就好了.... O(nlogn) -------------------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x)) #define foreach(i, x