CF335E Counting Skyscrapers 题解
提供一种最劣解第一且巨大难写的做法(
Bob
显然真正的楼量可以达到 ,是没办法直接做的,再加上唯一方案的样例,可以猜测有简单的结论。
考虑当楼高度为 时,每种高度对答案的贡献为 ,即 ,当楼高度为 时,每种高度对答案的贡献和为 ,即 。显然贡献都与 无关,也就是结论只和 有关,随便推几组数据再加上样例,容易猜出答案即为 。
Code:
void Sub2(){
scanf("%d%d",&n,&h);
printf("%d\n",n);
}
Alice
观察到高度 的楼本质上是一致的,可以钦定楼的高度区间为 (高度为 的概率变为 )。
设 号楼的高度最高,则 Bob 一定会经过 号楼。容易发现,在 号楼前,Bob经过的楼的高度单调不降, 号楼后单调不升。
注意,为了防止重复计算,若高度相同,钦定靠前的最高。
然后就可以转移了。
设 表示转移完第 栋楼,最后一个经过的 号楼的高度为 , 中最大高度为 ,是/否 经过最高的楼的期望。
设 表示转移完第 栋楼,最后一个经过的 号楼的高度为 , 中最大高度为 ,是/否 经过最高的楼的概率。
DP的时候用刷表法,枚举第 栋楼的高度转移即可。时间复杂度 要卡亿点常并滚动数组。
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本质上是分了 三个区间分别转移行和列,考虑将决策点放到线段树上,则问题转换为了线段树的区间修改/单点查询,时间复杂度 。
但由于要对 、是否经过最高楼、行或列分类讨论,所以要开 棵线段树,本地极限数据要跑 ,没有实质效果。
能不能更快?
观察到转移方程与 无关,考虑构造 的矩阵,跑矩阵快速幂即可,时间复杂度 ,可以通过本题,时间与空间上都更优。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通