Codeforces Beta Round #50 First Digit Law(概率dp+數位dp)

題目

給你N(N<=1e3)個區間,第i個區間[Li,Ri](1<=Li<=Ri<=1e18)

依次在第i個區間裏等概率地選擇一個數,

如果選擇的數是1開頭的,則稱爲這次是好的選擇

求好的選擇在N次選擇中至少佔K%(0<=K<=100)的概率

題解

在數位dp這裏還是有很多不足,回頭需要再刷

如果能求出,對於第i個選擇是好的選擇的概率one,

則記dp[i][j]爲前i個區間裏好的選擇佔了j個的概率,dp[i][j]=dp[i-1][j]*one+dp[i-1][j-1]*none

最後滿足j>=n*K%的dp[n][j]的概率和即爲所求

 

one的求法需要用數位dp,前綴和cal(r)-cal(l-1)

dfs傳三個參,其中pre=-1代表現在還在前導0階段即沒填數,pre=i代表最後一個填的是i

lim是正常的是否受上界影響,具體記憶化時,只有18個前綴會需要單獨計算

用-1去搜1和-1的情況,用1去搜i的情況,就能保證開頭一定是1了

對於之前還沒填的情況,要麼這位不填,要麼這位填1

對於之前已經填過首位1的情況,這位填的不超過上界即可

判斷什麼時候需要記憶化,對於已經出現開頭1來說,後續方案數只與位數有關,因此不受最高位限制即可

數位dp部分用for循環硬湊亦可,這裏補數位dp的寫法

代碼

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
typedef long long ll;
int n,k,len,mn;
ll l,r,a[20],sz[20];
double ans,dp[N][N],one,none;

//pre==-1代表沒填 等價於前導0 
//保證了pre=-1只會搜出pre=1和pre=-1
//pre=1再往下搜纔會搜出pre=i 所以底層情況都合法 
//滿足lim==1的 只有數的18個前綴 
ll dfs(int pos,int pre,bool lim)
{
	if(pos==0)return 1;
	if(~sz[pos] && ~pre && !lim)return sz[pos];
	ll ret=0;
	int up=lim?a[pos]:9;
	if(pre==-1)ret+=dfs(pos-1,1,lim&&a[pos]==1)+dfs(pos-1,-1,0);
	else
	{ 
		for(int i=0;i<=up;++i)
			ret+=dfs(pos-1,i,lim&&(i==up));
	}
	return !lim?sz[pos]=ret:ret;//注意到方案數只受非lim限制不受前導0限制 
}

ll part(ll x)
{
	len=0;
	while(x)a[++len]=x%10,x/=10;
	memset(sz,-1,sizeof sz);
	return dfs(len,-1,1); 
}

int main()
{
	scanf("%d",&n);
	dp[0][0]=1;
	for(int i=1;i<=n;++i)
	{
		scanf("%lld%lld",&l,&r);
		one=(part(r)-part(l-1))*1.0/(r-l+1.0);
		none=1-one;
		dp[i][0]=dp[i-1][0]*none;
		for(int j=1;j<=i;++j)
		dp[i][j]=dp[i-1][j]*none+dp[i-1][j-1]*one; 
	}
	scanf("%d",&k);
	mn=(n*k+99)/100;
	for(int i=mn;i<=n;++i)
	ans+=dp[n][i];
	printf("%.10lf\n",ans);
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章