T1 统计数字

题目

【题目描述】

设 S(N ) 表示 N 的各位数字之和,如 S(484) = 4+8+4 = 16, S(22) = 2+2 = 4。

如果一个正整数满足 S(x*x) = S(x) *S(x),我们称之为 Rabbit N umber。比方说,22 就是一个 Rabbit N umber,因为 S(484) = S(22) *S(22)。

现在,给出一个区间 [L, R],求在该区间内的 Rabbit N umber 的个数。

【输入格式】

输入仅一行,为空格隔开的两个数 L 和 R。

【输出格式】

输出仅一行一个整数,表示所求 Rabbit N umber 的个数。

【输入样例】

58 484

【输出样例】

24 

【数据规模】

1 <= L <= R <= 10^9

解析

看完题目第一反应:从L枚举到R,依次判断每个数是不是Rabbit N umber。然而数据规模是109,显然超时。

不过没事,打完暴力后,随便试一些数字,看看有没有什么规律。

1~1000内的Rabbit N umber如下:

1 2 3 10 11 12 13 20 21 22 30 31 100 101 102 103 110 111 112 113 120 121 122 130 200 201 202 210 211 212 220 221 300 301 310 311 1000

不难发现,无论是哪一位上,都没有大于3的数字(更大的范围内也是,可以自己试试),至于为什么,这里便不给出详细证明了(因为本蒟蒻不会)。

所以便有了剪纸:任何位上有大于3的数字就跳过。

于是便有了两种做法:

  1. dfs+剪纸
  2. 打表+二分

打表这里就不说了(太麻烦),这里来讲dfs做法:

dfs(int temp):temp可以理解为当前数是由temp+一个新的个位数组成的数,具体看代码就懂了。

从temp=0开始搜,每次dfs函数里处理个位为0 1 2 3的数,满足条件且在L~R的范围内就累加个数,

处理完后,如果数字小于等于R/10的话,就dfs(x)(即还可以增加位数)。

最后输出总个数就好了,别忘了开long long,至于S(x),直接模拟就好了。

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
using namespace std;
long long read()
{
    long long num=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        num=(num<<1)+(num<<3)+ch-'0';
        ch=getchar();
    }
    return num*w;
}
long long l,r,ans;
int S(long long x)
{
    int n=0;
    while(x>0)
    {
        n+=x%10;
        x/=10;    
    }
    return n;
}
void dfs(int temp)
{
    for(int i=0;i<=3;i++)
    {
        long long x=temp*10+i;
        int s=S(x);
        if(x==0||S(x*x)!=s*s) continue;
        if(l<=x&&r>=x) ans++;
        if(x<=r/10) dfs(x);
    }
}
int main()
{
    //freopen("rabbit.in","r",stdin);
    //freopen("rabbit.out","w",stdout);
    l=read(),r=read();
    dfs(0);
    cout<<ans;
    return 0;
}
View Code

相关文章: