[编程|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
时间限制: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<bits/stdc++.h>
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;
}