USACO - 【Odometer】中文题解

PS:题解来源于USACO官方,实际上是想练练翻译和表达能力,请不要在意细节,看不懂就继续看USACO的英文版题解吧……

题目:【传送门 http://www.usaco.org/index.php?page=viewproblem2&cpid=435】

官方题解:【传送门 http://www.usaco.org/current/data/sol_odometer_silver.html】

  题目大意:给出一个范围 X..Y(X<=Y<=10^18),求出该范围中“有趣数字”的个数。对“有趣数字”的定义是:一个数字去掉前导0后至少一半数位上的数字相同。例如3223和1100是,而97791和123不是。

  都明白 X..Y 中有趣的数字个数实际上等于 1..Y 中有趣数字的个数减去 1..X-1 中的数字个数吧?我们只要考虑如何求 1..a 中的个数 f(a) 即可。

  要求 f(a) ,我们可以通过自己构造数字的方法统计个数。因为要求数字中有一半以上的数位上的数字相同,我们只需枚举这个重复的数字是 0..9 的哪一个就是了,设为targ

  定义 F[i][und][k][is0]表示已经枚举了前 位数字,und 作为布尔值判断当前枚举的这个数字是否能保证小于aPascal的话und可以用01来表示),表示目前数字targ出现的次数与其他数字出现的次数之差,is0 作为布尔值表示当前是否在枚举数字的前导0F中计算的就是满足上述条件的数的个数。

  自然,可以枚举添加第i+1位数字,设为next。如果und = falsenext > a[i+1]a中第i+1位数),枚举得到的这个i+1位的数(下文称为now)就不合法了,因为在无法保证当前数字小于a(也就是说 1..i 个数位都和a对应的数位上数字相同)的情况下,不能添加一个比a对应数位上的数字还大的数(否则就可以肯定now >= a)。如果und = true 或者next < a[i+1] 那么now就可以保证小于a,定义x = true,否则x = false。如果next = 0is0 = true 就表示现在仍然在枚举前导0,定义y = true否则y = false。最后如果next = targ,就说明targ出现的次数与其他数字出现的次数差增大了1nk = k +1 否则 nk = k -1。最后F[i+1][x][nk][y] += F[i][und][k][is0]。初值F[0][false][0][true] = 1,答案很容易想到是Sum(F[n][x][k][false])n表示a的位数,x既可以是true也可以是falsek >= 0 保证targ出现次数大于n的一半。

  实际上,有一种情况我们计数重复,就是当位数为偶数时数字刚好由两个数字组成(如3322144141等),这些数计了2次。怎么计算这些数字的个数呢?因为位数最大只有18,我们完全可以考虑通过枚举targ1targ2的方法枚举这些数字,应该不会超时。或者可以重复使用相同的动态规划方程F[i][und][k][is0],只不过next的取值只有2个,且答案只能是Sum(F[n][x][0][false])。减掉就是 f(a) 的数值了。

  Pascal代码如下:

const
    maxn=40;
    st=20;
var
    f:array[0..maxn,0..1,0..maxn+1,0..1] of int64;
    a,b:string;
    x,y:int64;
function dp(v:string;l,x,y:longint):int64;
    var
        i,j,k,z,nx,nj,nk,nz:longint;
        now:int64;
    begin
        fillchar(f,sizeof(f),0);
        f[0,0,st,1]:=1;
        for i:=0 to l-1 do
            for j:=0 to 1 do
                for k:=0 to maxn do
                    for z:=0 to 1 do begin
                        now:=f[i,j,k,z];
                        if now=0 then continue;
                        for nx:=0 to 9 do begin
                            if (y>-1) and ((nx>0) or (z=0)) and (nx<>x) and (nx<>y) then continue;
                            if (j=0) and (nx>ord(v[i+1])-48) then continue;
                            if (z=1) and (nx=0) then nz:=1 else nz:=0;
                            if (nx

你可能感兴趣的:(USACO - 【Odometer】中文题解)