AtCoder Beginner Contest 189 F

这题有数学做法,但 Feecle6418 - AtCoder 提供了一种思路清奇的DP。

我们第一思路是设dp(i)为Takahashi到i时的期望步数,枚举上一次所在的位置,从dp(j)转移。

将整个流程反过来。即设dp(i)为从i开始,走到(n,inf)的期望步数。重新整理一下转移:

  1. i>n,则f(i)=0
  2. iA,则f(i)=1+1mj=1mf(i+j)
  3. iA,则f(i)=f(0)

仔细观察上面的转移,虽然它有环,但是它好像只有f(0)一个数是未知的。出现一个未知的数时我们不妨保留它,就像解方程中的x一样,看看后面能否找到登录关系。

保留f(0)之后,我们惊喜的发现,f(i)可以看成一个关于f(0)的代数式,且这个代数式的次数就是1

我们不妨设f(i)=kif(0)+bi,只转移kb就可以了。直接转移是O(nm)的,但是可以通过前缀和优化到O(n+m)

最终答案是f(0)。可以发现,f(0)也可以通过上面方法递推得到,故我们可以得到等式f(0)=k0f(0)+b0,解这个方程就可以得到f(0)

时间复杂度为O(n)

#include<bits/stdc++.h>
const int maxn=100005;
int n,m,k;
int a[maxn];
struct kb {
	double k,b;
}dp[maxn],sum;
int main() {
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=k;i++) {
		int pos;
		scanf("%d",&pos);
		a[pos]=1;
	}
	for(int i=1;i<=n;i++) {
		int j=i;
		while(a[j]) j++;
		if(j-i>=m) {
			puts("-1");
			return 0;
		}
	}
	dp[n]=(kb){0,0};
	sum=dp[n];
	for(int i=n-1;;i--) {
		if(a[i]) dp[i]=(kb){1,0};
		else dp[i]=(kb){sum.k/double(m),sum.b/double(m)+1};
		if(!i) break;
		sum.k+=dp[i].k,sum.b+=dp[i].b;
		if(i+m<=n) sum.k-=dp[i+m].k,sum.b-=dp[i+m].b;
	}
	double res=(-dp[0].b)/(dp[0].k-1);
	printf("%.10lf\n",res);
	return 0;
}
posted @   Nastia  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示