ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF335E Counting Skyscrapers 题解

2022-05-30 14:01:22  阅读:140  来源: 互联网

标签:号楼 int 题解 高度 double maxh CF335E Counting iii


提供一种最劣解第一且巨大难写的做法(

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)\),可以通过本题,时间与空间上都更优。

标签:号楼,int,题解,高度,double,maxh,CF335E,Counting,iii
来源: https://www.cnblogs.com/lzqy/p/16326528.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有