2013年多校的题目,那个时候不太懂怎么做,最近重新拾起来,看了一下出题人当初的解题报告,再结合一下各种情况的理解,终于知道整个大致的做法,这里具体写一下做法。

题意:给你一段长度为[1..n]的白色区间,每次随机的取一个子区间将这个区间涂黑,问整个区间被涂黑时需要的期望次数。

1. 首先要做的是一个题目的转化。如果我定义pi为 恰好i次将区间涂黑的概率,那么显然期望 E= 0*p0+1*p1+2*p2+... 换一种角度看这个公式,其实这个公式可以这么写

        E = p1 + p2 + p3 + p4 + p5 + ...

                  p2 + p3 + p4 + p5 + ...

                   p3 + p4 + p5 + ...

定义Li=p(i+1) + p(i+2) + ... 那么我们可以把Li理解成覆盖了i次都没有将整个区间涂黑的概率。

所以E=L0+L1+L2+...

2. 理解了上面这一点之后我们可以先考虑一种暴力的做法,那就是枚举2^n个最后留下来的点,譬如最后留下来的点是v1,v2,v3...vk,那么实际上可选的区间只有[1,v1-1],[v1+1,v2-1],[v2+1,v3-1]...那么这里区间的选法就有A种(A是可以求出来的),要使这些点都能被留下来,那么我们每次选的只能是这A个区间,因而概率 p = A/ ((n+1)*n/2). 那么对于Li来说 Li += p^i* (-1)^(k-1).  之所以要乘(-1)^k是因为这里需要有一个容斥。考虑到p^i的累加和即 p+p^1+p^2+... = 1/(1-p) 所以对于每个2^n,它对最后期望的影响实际上是 1/(1-p)*(-1)^(k-1).

3. 暴力的想法给到了,那么下面就是要优化一下这种做法了,因为我总不可能2^50枚举所有剩下的点。一个自然的想法是考虑dp,看上面的式子我们不难发现实际上最后起作用的是 A和k的奇偶性,而A的范围是有限的(总的区间个数),那么我们可以定义一个状态dp[i][j][k]表示,第i个点是白点(未被覆盖),且前i个白点的奇偶性为j,可选区间的个数为k有多少个子集。 那么对于一个期望值E[n]来说(n>=i) 这个状态对E[n]的影响实际上是

可选的区间  A = 前i个点的可选区间数+ [i...n]这一段为黑的可选区间数 = k+(n-i+1)(n-i)/2;

      p = A/((n+1)*n/2);

      E[n] += dp[i][j][k]*(1-p)*(-1)^(j-1);

dp状态的转移具体可以参考一下下面的代码。

4. 题目的第二个坑点就在于它需要有15个小数点的精度,所以需要写一个高精度类,这里只需要涉及两个浮点数的加法和减法就可以了,所以写的时候还是挺容易的,注意答案是round到15个小数点,所以如果小数点后第16个数是>=5的,输出之前还要将结果加上一个1e-15.

下面贴两分代码,一份是不需要高精度的时候的dp的代码,另外一份是要高精度的代码。

#pragma warning(disable:4996)
#include<algorithm>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<queue>
#include<list>
#include<time.h>
#include<bitset>
using namespace std; #define ll long long
#define maxn 55 ll dp[maxn][2][maxn*(maxn + 1) / 2];
double E[maxn]; int main()
{
memset(dp, 0, sizeof(dp));
memset(E, 0, sizeof(E));
dp[0][0][0] = 1;
for (int i = 0; i <= 50; ++i){
for (int j = 0; j <= (i + 1)*i / 2; ++j){
if (dp[i][0][j]) {
for (int k = i + 1; k <= 50; ++k){
dp[k][1][j + (k - i)*(k - i - 1) / 2] += dp[i][0][j];
}
}
if (dp[i][1][j]){
for (int k = i + 1; k <= 50; ++k){
dp[k][0][j + (k - i)*(k - i-1) / 2] += dp[i][1][j];
}
}
}
} for (int i = 1; i <= 50; ++i){
for (int k = 0; k <= i; ++k){
for (int j = 0; j <= (k + 1)*k / 2; ++j){
if (dp[k][0][j]){
long double p = (j + (i - k + 1)*(i - k) / 2 + .0) / ((i + 1)*i / 2);
if (p == 1.0) { continue; }
E[i] -= dp[k][0][j] / (1 - p);
}
if (dp[k][1][j]){
long double p = (j + (i - k + 1)*(i - k) / 2 + .0) / ((i + 1)*i / 2);
if (p == 1.0) { continue; }
E[i] += dp[k][1][j] / (1 - p);
}
}
}
}
int xx;
cin >> xx;
}

  

#pragma warning(disable:4996)
#include<algorithm>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<queue>
#include<list>
#include<time.h>
#include<bitset>
using namespace std; #define ll long long
#define maxn 55
#define maxdp 50 struct BigDecimal
{
ll ip;
int dp[maxdp];
BigDecimal(){
ip = 0;
memset(dp, 0, sizeof(dp));
}
BigDecimal(int x){
ip = x;
memset(dp, 0, sizeof(dp));
}
BigDecimal(ll x, ll y)
{
ip = x / y;
x = abs(x); y = abs(y);
x = x%y;
for (int i = 0; i < maxdp; ++i){
x *= 10;
dp[i] = x / y;
x %= y;
}
}
}; BigDecimal operator + (const BigDecimal &a, const BigDecimal &b)
{
BigDecimal ret;
int carry = 0;
for (int i = maxdp - 1; i >= 0; --i){
ret.dp[i] = (a.dp[i] + b.dp[i] + carry) % 10;
carry = (a.dp[i] + b.dp[i] + carry) / 10;
}
ret.ip = a.ip + b.ip + carry;
return ret;
} BigDecimal operator - (const BigDecimal &a, const BigDecimal &b)
{
BigDecimal ret;
int marry = 0;
for (int i = maxdp - 1; i >= 0; --i){
ret.dp[i] = (a.dp[i] - b.dp[i] - marry + 10) % 10;
marry = a.dp[i] - b.dp[i] - marry < 0 ? 1 : 0;
}
ret.ip = a.ip - b.ip - marry;
return ret;
} void print(const BigDecimal& x, int digits)
{
printf("%I64d.", x.ip);
BigDecimal y;
if (x.dp[digits] >= 5){
y.dp[digits - 1] = 1;
}
y = x + y;
for (int i = 0; i < digits; ++i){
printf("%d", y.dp[i]);
}
puts("");
} ll dp[maxn][2][maxn*(maxn + 1) / 2];
BigDecimal Ep[maxn];
BigDecimal En[maxn];
BigDecimal E[maxn]; int main()
{
memset(dp, 0, sizeof(dp));
memset(E, 0, sizeof(E));
dp[0][0][0] = 1;
for (int i = 0; i <= 50; ++i){
for (int j = 0; j <= (i + 1)*i / 2; ++j){
if (dp[i][0][j]) {
for (int k = i + 1; k <= 50; ++k){
dp[k][1][j + (k - i)*(k - i - 1) / 2] += dp[i][0][j];
}
}
if (dp[i][1][j]){
for (int k = i + 1; k <= 50; ++k){
dp[k][0][j + (k - i)*(k - i - 1) / 2] += dp[i][1][j];
}
}
}
} for (int i = 1; i <= 50; ++i){
ll all = ((i + 1)*i / 2);
for (int k = 0; k <= i; ++k){
for (int j = 0; j <= (k + 1)*k / 2; ++j){
if (dp[k][0][j]){
ll select = j + (i - k + 1)*(i - k) / 2;
if (select == all) continue;
En[i] = En[i] + BigDecimal(all*dp[k][0][j], all - select);
}
if (dp[k][1][j]){
ll select = j + (i - k + 1)*(i - k) / 2;
if (select == all) continue;
Ep[i] = Ep[i] + BigDecimal(all*dp[k][1][j], all - select);
}
}
}
E[i] = Ep[i] - En[i];
}
int T; cin >> T; int n;
while (T--)
{
cin >> n;
print(E[n], 15);
}
return 0;
}

  

HDU4624 Endless Spin(概率&&dp)的更多相关文章

  1. HDU4624 Endless Spin 和 HAOI2015 按位或

    Endless Spin 给你一段长度为[1..n]的白色区间,每次随机的取一个子区间将这个区间涂黑,问整个区间被涂黑时需要的期望次数. n<=50 题解 显然是min-max容斥,但是n的范围 ...

  2. HDU4624 Endless Spin 【最大最小反演】【期望DP】

    题目分析: 题目是求$E(MAX_{i=1}^n(ai))$, 它等于$E(\sum_{s \subset S}{(-1)^{|s|-1}*min(s))} = \sum_{s \subset S}{ ...

  3. 题解 hdu4624 Endless Spin

    题目链接 题目大意: 有长度为\(n\)的区间,每次随机选择一段(左右端点都是整数)染黑,问期望多少次全部染黑. \(n\leq 50\) 设\(n\)个随机变量\(t_1,...,t_n\).\(t ...

  4. Codeforces 28C [概率DP]

    /* 大连热身D题 题意: 有n个人,m个浴室每个浴室有ai个喷头,每个人等概率得选择一个浴室. 每个浴室的人都在喷头前边排队,而且每个浴室内保证大家都尽可能均匀得在喷头后边排队. 求所有浴室中最长队 ...

  5. HDU 4405 Aeroplane chess (概率DP)

    题意:你从0开始,要跳到 n 这个位置,如果当前位置是一个飞行点,那么可以跳过去,要不然就只能掷骰子,问你要掷的次数数学期望,到达或者超过n. 析:概率DP,dp[i] 表示从 i  这个位置到达 n ...

  6. POJ 2096 Collecting Bugs (概率DP)

    题意:给定 n 类bug,和 s 个子系统,每天可以找出一个bug,求找出 n 类型的bug,并且 s 个都至少有一个的期望是多少. 析:应该是一个很简单的概率DP,dp[i][j] 表示已经从 j ...

  7. POJ 2151 Check the difficulty of problems (概率DP)

    题意:ACM比赛中,共M道题,T个队,pij表示第i队解出第j题的概率 ,求每队至少解出一题且冠军队至少解出N道题的概率. 析:概率DP,dp[i][j][k] 表示第 i 个队伍,前 j 个题,解出 ...

  8. 概率DP light oj 1030

    t组数据 n块黄金 到这里就捡起来 出发点1 到n结束  点+位置>n 重掷一次 dp[i] 代表到这里的概率 dp[i]=(dp[i-1]+dp[i-2]... )/6  如果满6个的话 否则 ...

  9. hdu 4050 2011北京赛区网络赛K 概率dp ***

    题目:给出1-n连续的方格,从0开始,每一个格子有4个状态,左右脚交替,向右跳,而且每一步的步长必须在给定的区间之内.当跳出n个格子或者没有格子可以跳的时候就结束了,求出游戏的期望步数 0:表示不能到 ...

随机推荐

  1. Cortex-M0(+)内核的处理器架构简介

    Cortex-M0(+)内核的处理器架构简介 2015年03月02日 16:51:12 阅读数:3158 系统架构 Cortex-M0处理器具有32位系统总线接口,以及32位地址线,即有4GB的地址空 ...

  2. java代码异常篇

    总结:掌握流.缓冲区类的方法 package com.b; import java.io.BufferedReader; import java.io.File; import java.io.Fil ...

  3. 第十四届华中科技大学程序设计竞赛决赛同步赛 A - Beauty of Trees

    A - Beauty of Trees 题意: 链接:https://www.nowcoder.com/acm/contest/119/A来源:牛客网 Beauty of Trees 时间限制:C/C ...

  4. leetcode482

    这道题主要使用了C++的几个API,大小写转化,字符串替换.其余的逻辑都比较简单.而且经查资料,string类字符串拼接的速度使用+=的速度是很快的.以下代码,也是用的+=来拼接字符串. string ...

  5. js和jQuery判断数组是否包含指定元素

    最近遇见一些前台基础性问题,在这里笔者觉得有必要记录一下,为了以后自己查阅或者读者查看. 已知var arr = ['java','js','php','C++']; 问题:arr数组是否包含‘jav ...

  6. DAY11-MYSQL补充之SQL逻辑查询语句执行顺序

    一 SELECT语句关键字的定义顺序 SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOI ...

  7. DAY5-模块与包

    一.模块的介绍 1.什么是模块 #常见的场景:一个模块就是一个包含了一组功能的python文件,比如spam.py,模块名为spam,可以通过import spam使用. #在python中,模块的使 ...

  8. 数据库监听。数据库一次notify,Activity多次接收

    今天项目中发现一个bug: (1)当uri数据库中有更新,会从数据库层DataService中通知应用层,调用notifyChange: mContext.getContentResolver().n ...

  9. 【oracle】首次启动SQL Developer配置java.exe出错(Could not find jvm.cfg! )

    1.环境 win7/8/8.1  x64,Oracle 11g r2,jdk7 x64 2.问题 第一次启动Oracle SQL Developer的时候会让我们填写Java.exe的路径,我在jdk ...

  10. SpringBoot12 QueryDSL02之利用QueryDSL实现多表关联查询

    1 业务需求 有的系统业务逻辑比较复杂,存在着多表关联查询的的情况,查询的内容不仅仅是单张表的的内容而是多张表的字段组合而成的,直接使用SplringDataJPA实现是比较复杂的,但是如果使用Que ...