CF335E Counting Skyscrapers 题解
提供一种最劣解第一且巨大难写的做法(
Bob
显然真正的楼量可以达到 \(314!\),是没办法直接做的,再加上唯一方案的样例,可以猜测有简单的结论。
考虑当楼高度为 \(k(k<h)\) 时,每种高度对答案的贡献为 \(2^{k-1}\times 2^{-k}\),即 \(\frac{1}{2}\),当楼高度为 \(k(k \ge h)\) 时,每种高度对答案的贡献和为 \(2^k\times 2^{-k}\),即 \(1\)。显然贡献都与 \(h\) 无关,也就是结论只和 \(n\) 有关,随便推几组数据再加上样例,容易猜出答案即为 \(n\)。
Code:
void Sub2(){
scanf("%d%d",&n,&h);
printf("%d\n",n);
}
Alice
观察到高度 \(\ge h\) 的楼本质上是一致的,可以钦定楼的高度区间为 \([1,h]\)(高度为 \(h\) 的概率变为 \(2^{1-h}\))。
设 \(k\) 号楼的高度最高,则 Bob 一定会经过 \(k\) 号楼。容易发现,在 \(k\) 号楼前,Bob经过的楼的高度单调不降,\(k\) 号楼后单调不升。
注意,为了防止重复计算,若高度相同,钦定靠前的最高。
然后就可以转移了。
设 \(f_{i,j,k,0/1}\) 表示转移完第 \(i\) 栋楼,最后一个经过的 \(x\) 号楼的高度为 \(j\),\([x+1,i]\) 中最大高度为 \(k\),是/否 经过最高的楼的期望。
设 \(g_{i,j,k,0/1}\) 表示转移完第 \(i\) 栋楼,最后一个经过的 \(x\) 号楼的高度为 \(j\),\([x+1,i]\) 中最大高度为 \(k\),是/否 经过最高的楼的概率。
DP的时候用刷表法,枚举第 \(i\) 栋楼的高度转移即可。时间复杂度 \(O(nh^3)\) 要卡亿点常并滚动数组。
Code:
const double eps=1e-15;
const int maxn=30010;
const int maxh=35;
int n,h;
double g[2][maxh][maxh][2];
double f[2][maxh][maxh][2];
double V[maxh],S[maxh];
void Sub1(){
scanf("%d%d",&n,&h);
for(int i=0;i<h;i++){
V[i]=1.0/(1<<i+1),S[i]=(1-1.0/(1<<i));
f[1][i][0][0]=V[i]+0.5,g[1][i][0][0]=V[i];
}
if(h) V[h]=V[h-1],S[h]=(1-1.0/(1<<h));
else V[h]=1;
for(int i=0;i<=h;i++) f[1][i][0][1]=V[i],g[1][i][0][1]=V[i];
//以上是初始化
double tmp1,tmp2,tmp;
for(int i=1,ii=1,iii=0;i<n;i++,ii^=1,iii^=1){
memset(f[iii],0,sizeof(f[iii]));
memset(g[iii],0,sizeof(g[iii]));
//注意,为了处理当j=i时(Bob经过当前楼)k也为0的情况
//这里枚举的k,u都加了1(j=0时k才为0)
//为了卡常,转移的式子很丑,但本质是一样的
for(int j=0;j<=h;j++)
for(int k=0;k<=j;k++){
tmp1=f[ii][j][k][0],tmp2=g[ii][j][k][0],tmp=tmp2/2;
if(tmp1>eps){
//u<=k
f[iii][j][k][0]+=tmp1*S[k];
g[iii][j][k][0]+=tmp2*S[k];
tmp1*=V[k],tmp2*=V[k];
//--------
for(int u=k+1;u<=h+1;u++){
if(u-1>=j) f[iii][u-1][0][0]+=tmp1+tmp,g[iii][u-1][0][0]+=tmp2;
else f[iii][j][u][0]+=tmp1,g[iii][j][u][0]+=tmp2;
if(u-1>j) f[iii][u-1][0][1]+=tmp1,g[iii][u-1][0][1]+=tmp2;
if(u<h) tmp1/=2,tmp2/=2;
}
f[iii][h][0][0]+=tmp;
}
tmp1=f[ii][j][k][1],tmp2=g[ii][j][k][1],tmp=tmp2/2;
if(tmp1>eps){
//u<=k
f[iii][j][k][1]+=tmp1*S[k];
g[iii][j][k][1]+=tmp2*S[k];
tmp1*=V[k],tmp2*=V[k];
//--------
for(int u=k+1;u<=j+1;u++){
f[iii][u-1][0][1]+=tmp1+tmp,g[iii][u-1][0][1]+=tmp2;
if(u-1<j) f[iii][j][u][1]+=tmp1,g[iii][j][u][1]+=tmp2;
if(u<h) tmp1/=2,tmp2/=2;
}
if(j==h) f[iii][h][0][1]+=tmp;
}
}
}
double ans=0;
for(int i=0;i<=h;i++)
ans+=f[n&1][i][0][1];
printf("%.9lf",ans);
}
能不能更快?
上述期望DP本质上是分了 \([1,k],[k+1,j][j+1,h]\) 三个区间分别转移行和列,考虑将决策点放到线段树上,则问题转换为了线段树的区间修改/单点查询,时间复杂度 \(O(nh^2\log h)\)。
但由于要对 \(f,g\)、是否经过最高楼、行或列分类讨论,所以要开 \(8nh\) 棵线段树,本地极限数据要跑 \(10s+\),没有实质效果。
能不能更快?
观察到转移方程与 \(n\) 无关,考虑构造 \(h\times h\) 的矩阵,跑矩阵快速幂即可,时间复杂度 \(O(h^3logn)\),可以通过本题,时间与空间上都更优。
CF335E Counting Skyscrapers 题解的更多相关文章
- 【SP26073】DIVCNT1 - Counting Divisors 题解
题目描述 定义 \(d(n)\) 为 \(n\) 的正因数的个数,比如 \(d(2) = 2, d(6) = 4\). 令 $ S_1(n) = \sum_{i=1}^n d(i) $ 给定 \(n\ ...
- 【codeforces 335E】 Counting Skyscrapers
http://codeforces.com/problemset/problem/335/E (题目链接) 题意 懒得写了= = Solution 这题咋不上天= =. 参考题解:http://blo ...
- 【CF335 E】Counting Skyscrapers
题意 有一排高楼,每一栋高楼有一个正整数高度,高度为 \(i\) 的概率为 \(2^{-i}\).一栋楼的每层从下往上依次编号为 \(0,1,2,\cdots,i-1\). 为了出题,大楼之间安装了溜 ...
- P4778 Counting Swaps 题解
第一道 A 掉的严格意义上的组合计数题,特来纪念一发. 第一次真正接触到这种类型的题,给人感觉好像思维得很发散才行-- 对于一个排列 \(p_1,p_2,\dots,p_n\),对于每个 \(i\) ...
- POJ 2386 Lake Counting 搜索题解
简单的深度搜索就能够了,看见有人说什么使用并查集,那简直是大算法小用了. 由于能够深搜而不用回溯.故此效率就是O(N*M)了. 技巧就是添加一个标志P,每次搜索到池塘,即有W字母,那么就觉得搜索到一个 ...
- CF908A New Year and Counting Cards 题解
Content 有 \(n\) 张卡牌,每张卡牌上只会有大小写字母和 \(0\sim 9\) 的阿拉伯数字.有这样一个描述:"如果卡牌正面写有元音字母(\(\texttt{A,E,I,O,U ...
- The 2013 South America/Brazil Regional Contest 题解
A: UVALive 6525 cid=61196#problem/A" style="color:blue; text-decoration:none">Atta ...
- Codeforces 1138 - A/B/C/D/E - (Undone)
链接:https://codeforces.com/contest/1137 A - Skyscrapers 题解:对于每一段 $1$ 和每一段 $2$,统计他们的长度.因此对于相邻的两段长度求较小值 ...
- 【题解】Counting D-sets(容斥+欧拉定理)
[题解]Counting D-sets(容斥+欧拉定理) 没时间写先咕咕咕. vjCodeChef - CNTDSETS 就是容斥,只是难了一二三四五\(\dots \inf\)点 题目大意: 给定你 ...
随机推荐
- 前端学习02:jQuery 日历
引言:学习前端已经接近1个月了,先后经历了1周的 html+css, 2周的"JavaScript 从入门到下跪",期间还看了vue+webpack.然鹅,Mentor Brigh ...
- mixin和composition api
1. 这两个都是实现组件逻辑复用的法宝 2. composition api是vue3的, composition api的出现就是解决mixins的不足之处的 一. mixin 回顾下mixin, ...
- Python入门-面向对象三大特性-封装
一.封装 封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容. 所以,在使用面向对象的封装特性时,需要: 将内容封装到某处 从某处调用被封装的内容 第一步:将内容封装到某处 sel ...
- pod和容器(容易混淆的地方)
在Kubenetes中,所有的容器均在 pod 中运行,一个pod可以承载一个或者多个相关的docker容器(或rkt,以及用户自定义容器),同一个Pod中的容器可以部署在同一个物理机器(可以叫宿主机 ...
- Java学习day2
今天学习了Java的数据类型.运算符.选择循环结构以及数组. Java所有的关键字全部小写 Java的数据类型与c语言相似,但是c语言定义数组时可以不主动对其初始化,而Java则必须先初始化,有动态和 ...
- vue3组合式API
vue3组合式API 为什么要用组合式API,我们来看看它是如何解决vue2的局限性的 1.vue2的局限性 当组件内容越来越多,逻辑越来越复杂,可读性就会降低,并且难以维护. vue2组件采用配置式 ...
- 2021.07.19 BZOJ2654 tree(生成树)
2021.07.19 BZOJ2654 tree(生成树) tree - 黑暗爆炸 2654 - Virtual Judge (vjudge.net) 重点: 1.生成树的本质 2.二分 题意: 有一 ...
- Confluent之Kafka Connector初体验
概述 背景 Apache Kafka 是最大.最成功的开源项目之一,可以说是无人不知无人不晓,在前面的文章<Apache Kafka分布式流处理平台及大厂面试宝典>我们也充分认识了Kafk ...
- Vue2响应式原理
vue2响应式原理 vue的特性:数据驱动视图和双向数据绑定.vue官方文档也提供了响应式原理的解释: 深入响应式原理 Object.defineProperty() Object.definePro ...
- Google广告批量投放脚本
该脚本文件是一个转化文件,将编辑的Google投广告模板转化成Google批量投放广告的脚本文件,实现批量投放广告,节省投广时间.PS:要与相应的模板结合使用,投广告模板在本人博客的文件中,可以下载. ...