【赛后补题】Lucky Probability(CodeForces 110D)

题意

给定两个\(P,Q\)的正整数区间(\(P,Q\)都符合\([L,R]\)这个区间,并且都\(\le 10^9\)),分别从其中随机选出一个数,选出的两个数作为一个新区间的左右端点。要求新区间内的幸运数刚好为\(k\)个的概率(幸运数指一个数的数位只有4或7)。

分析

这题要思考着做。首先能有一个直觉:在\(10^9\)中间的幸运数肯定不多(2^10左右)。这个可以暴力求出。然后概率如何求?所有的情况一定是\((P_r-P_l+1)(Q_r-Q_l+1)\)这么多,然后符合条件的幸运数区间一共有\(l_tot-k+1\)个(\([Lucky_{i},Lucky_{i+k-1}]\))这么多。为了计算所有情况,我们只能遍历所有幸运数区间,看在什么情况下能符合题意(显然不会去遍历\(10^9\)的P、Q的区间)。对于每个这样的\([Lucky_{i},Lucky_{i+k-1}]\)区间,能够与他们相交的P、Q是存在两种情况的:a)P<Q;b) P>Q。我们分类讨论即可。简单地说,先计算Lucky区间与\([P_l,P_r]\)的交集(相当于在a情况下考虑区间头),然后再计算Lucky区间与\([Q_l,Q_r]\)的交集,将两个结果相乘即是符合第i个幸运区间的可能情况。同样地,还要对反向地(即PQ交换)再计算一遍。注意对\(k=1\)情况的特判。
遍历所有的幸运区间后,概率就不难求得了。

代码(Java)

/*
 * ACM Code => cf110d.java
 * Written by Sam X
 * Date: 三月, 08, 2019
 * Time: 14:27
 */
import java.util.*;
import java.math.*;

public class cf110d
{
    static ArrayList<Long> vec = new ArrayList<>();
    static void dfs(long x)
    {
        if(x>1e9) return;
        if(x*10+4<1e9)
        {
            vec.add(x*10+4);
            dfs(x*10+4);
        }
        if(x*10+7<1e9)
        {
            vec.add(x*10+7);
            dfs(x*10+7);
        }
    }
    static final long contain(long x1, long y1, long x2, long y2)
    {
        return Math.max(Math.min(y1,y2)-Math.max(x1,x2)+1,0l);
    }
    public static void main(String args[])
    {
        Scanner cin = new Scanner(System.in);
        long pl = cin.nextLong(), 
             pr = cin.nextLong(), 
             vl = cin.nextLong(),
             vr = cin.nextLong(),
             k = cin.nextLong();
    
        dfs(0);
        vec.add(0l);
        vec.add((long)1e9);

        Collections.sort(vec);
        /*
        for(long x: vec)
        {
            System.out.print(x+" ");
        }
        System.out.println();
        */
        long ans=0;

        int sz=vec.size()-2;
        for(int i=1; i<=sz-k+1; ++i)
        {
            int j=i+(int)k-1;
            ans+=contain(vec.get(i-1)+1, vec.get(i), pl, pr)*contain(vec.get(j), vec.get(j+1)-1, vl, vr);
            if(vec.get(i)>pr) break;
        }

        for(int i=1; i<=sz-k+1; ++i)
        {
            int j=i+(int)k-1;
            ans+=contain(vec.get(i-1)+1, vec.get(i), vl, vr)*contain(vec.get(j), vec.get(j+1)-1, pl, pr);
            if(vec.get(i)>vr) break;
        }

        if(k==1)
        {
            for(int i=1; i<=sz; ++i)
            {
                if(contain(vec.get(i), vec.get(i), pl, pr)!=0 &&
                   contain(vec.get(i), vec.get(i), vl, vr)!=0) ans--;
            }
        }

        System.out.printf("%.12f\n", (double)ans/(vr-vl+1)/(pr-pl+1));
        cin.close();
    }
}
posted @ 2019-03-11 18:29  ISoLT  阅读(238)  评论(0编辑  收藏  举报