CodeForces 110D——Lucky Probability(dfs+思维)

题意: 

出个两个(P,V)范围在1e9的正整数区间,分别从其中随机选出一个数,选出的两个数作为一个新区间的左右端点。要求新区间内的幸运数刚好为k个的概率(幸运数指一个数的数位只有4或7)。

思路: 

首先可以dfs找出所有在数据范围内的幸运数,记为L数组,因为数目不多,便可成k段遍历 L数组,每次分别计算【 L[i-1]+1,L[i]】与【Pl,Pr】的线段交集,和【 L[i+k-1],L[i+k]-1】与【Vl,Vr】的线段交集,将两个结果相乘即是符合[i,i+k-1]区间幸运数的可能情况,同样的还要对P,V两个区间交换后在计算一遍,概率=所有可能情况/(Pr-Pl+1)/(Vr-Vl+1);

然后还要注意k==1的情况会重复计算,需特判。

代码: 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll L[1050];
int num=0;
void dfs(int len,int p,int v)
{
    if(p==len)
    {
        L[++num]=v;
        return;
    }
    dfs(len,p+1,v*10+4);
    dfs(len,p+1,v*10+7);
}

ll contain(ll x1,ll y1,ll x2,ll y2)
{
    ll l=max(x1,x2);
    ll r=min(y1,y2);
    return r-l+1>0?r-l+1:0;
}

int main()
{
    for(int len=1;len<=9;len++) dfs(len,0,0);
    L[0]=0;
    L[++num]=1000000000;
    //for(int i=1;i<=num;i++) printf("%d  %lld\n",i,L[i]);
    ll Pl,Pr,Vl,Vr;
    int k;
    scanf("%lld%lld%lld%lld%d",&Pl,&Pr,&Vl,&Vr,&k);
    ll sum=0;
    for(int i=1;i<=1022-k+1;i++)
    {
        int j=i+k-1;
        sum+=contain(L[i-1]+1,L[i],Pl,Pr)*contain(L[j],L[j+1]-1,Vl,Vr);
        if(L[i]>Pr) break;
    }

    for(int i=1;i<=1022-k+1;i++)
    {
        int j=i+k-1;
        sum+=contain(L[i-1]+1,L[i],Vl,Vr)*contain(L[j],L[j+1]-1,Pl,Pr);
        if(L[i]>Vr) break;
    }

    if(k==1)
    {
        for(int i=1;i<=1022;i++)
        {
            if(contain(L[i],L[i],Pl,Pr)&&contain(L[i],L[i],Vl,Vr)) sum--;
        }
    }

    printf("%.10f\n",sum*1.0/(Vr-Vl+1)/(Pr-Pl+1));
}

猜你喜欢

转载自blog.csdn.net/QuincyTan/article/details/84573548