54 C First Digit Law
制約条件
n≦1000,
L[i],R[i]≦10^18
方針
区間[ L[i],R[i] ]に含まれる、先頭が1である数をdpにより求めればよい。
m以下の数で先頭が1であるような数の数は次のようなdpにより求められる。
dp[位置][先頭に1が出たか][mより小さいか]としてテーブルを更新していく。
doubleだと精度不足らしく落ちる。long doubleにしたら通った。
ソースコード
ll d[20][2][2]; //pos, one, smaller long double calc(ll m){ char s[20]; sprintf(s,"%I64d",m); int n=strlen(s); memset(d,0,sizeof(d)); d[0][0][0]=1; rep(i,n)rep(j,2)rep(k,2){ rep(l,10){ int none=j,nsm=k; if(j==0&&l==1)none=1; if(l<s[i]-'0')nsm=1; if(j==0&&l>1||!k&&l>s[i]-'0')continue; d[i+1][none][nsm]+=d[i][j][k]; } } return d[n][1][0]+d[n][1][1]; } long double dp[1001][1001]; void run() { int n,k; vector<long double> v; cin>>n; rep(i,n){ ll l,r; cin>>l>>r; v.pb((calc(r)-calc(l-1))/(r-l+1)); } cin>>k; memset(dp,0,sizeof(dp)); dp[0][0]=1; rep(i,n)rep(j,i+1){ dp[i+1][j+1]+=dp[i][j]*v[i]; dp[i+1][j]+=dp[i][j]*(1-v[i]); } long double ans=0; for(int i=(int)(ceil(n*k/100.0-EPS));i<=n;i++)ans+=dp[n][i]; cout<<setiosflags(ios::fixed)<<setprecision(20)<<ans<<endl; }