題目
給你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;
}