数论题 (牛客网)

[编程|1000分] 数码
时间限制:1秒
空间限制:32768K
题目描述
给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。
输入描述:
一行,两个整数 l 和 r (1 ≤ l ≤ r ≤ 10^9)。


输出描述:
输出9行。

第 i 行,输出数码 i 出现的次数。

输入例子:
1 4

输出例子:
4
2
1
1
0
0
0
0
0
题解: 这题你正面去求每个数的约数绝对超时,反过来想如果 a*b 属于[l, r]里的话a和b就是符合条件的两个约数;所以就从1开始枚举a,对于每个a都可以确定一个b的范围(大概是[l/a, r/a], 具体情况代码里讲), 这里还要注意一下不能重复枚举,就要保证a <= b。 然后就可以统计了,对于a来说它做了(r/a-l/a+1)次,所以 res[a的最高位] += (r/a-l/a+1);对于b来说, l/a到r/a里的每个数都出现了一次,你普通的去遍历每个数再求出最高位也会超时, 具体看我代码怎么写的(这段代码好恶心);
上代码:
#include
using namespace std;
#define LL long long
const int MAXN = 308;
const int MOD = 1e9;
int ff[10] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
LL res[10];

void addlr(int ll, int rr) {// 求上面所说的b;别看这代码短,很恶心,自己看吧;对了rr = 10^9时ff会越界,懒得改,反正能a;
    int tmp, i = 1, fir;
    while (ll <= rr) {
        tmp = ll%ff[i];
        if (!tmp) {i++; continue;}
        int k = (ff[i]-tmp)/ff[i-1];
        while (k-- && ll <= rr) {
            fir = ll;
            while (fir > 9)
                fir /= 10;
            res[fir] += ff[i-1];
            ll += ff[i-1];
        }
        i++;
    }
    res[fir] -= (ll-1-rr);
}

int main() {
    freopen("in.txt", "r", stdin);
    int l, r;
    scanf("%d%d", &l, &r);
    int mer = 1;
    while (1) {
        int ll = l/mer;//下面求左边界
        if (ll < mer) ll = mer;
        if (ll*mer < l) ll++;
        int rr = r/mer;//右边界
        if (ll > rr) break;//退出条件
        int tmp = mer;
        while (tmp > 9) tmp /= 10;//求mer的最高位
        res[tmp] += (rr-ll+1);//求上面题解所说的a的情况
        if (mer == ll) res[tmp]--;//这里要注意,如果 a*a 属于这个区间,a只能算一次,上面多算了,所以要减一个;
        addlr(ll, rr);
        mer++;
    }
    for(int i = 1; i < 10; i++)
        printf("%I64d\n", res[i]);
    return 0;
}



你可能感兴趣的:(数论)