题目链接:https://ac.nowcoder.com/acm/problem/13221
题意:给定两个整数lll和rrr ,对于所有满足1≤l≤x≤r≤1091 ≤ l ≤ x ≤ r ≤ 10^91≤l≤x≤r≤109的xxx ,把xxx 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~91~91~9每个数码出现的次数。
思路:
在讲这一题得写法之前,我们先要引入整除分块这个知识。
整除分块是求∑i=1nn/i\sum_{i=1}^{n}n/i∑i=1nn/i的和。
首先如果我们直接forforfor循环遍历一遍,那一般题目一定会超时。还是遵循遇事不决先打表的原则,我们将n=10n=10n=10带进去模拟一下,可以得到如下表所示的值:
我们可看到有很多的之都是一样的,那我们是不是就可以将所有n/in/in/i相等的放在“一块”呢?那我们可以定义两个变量l,rl,rl,r,用lll表示这一块的左区间,rrr表示这一块的右区间,所以lll我们要从一开始遍历,每次令l=r+1l = r + 1l=r+1,那rrr呢?我们在草稿纸上算一下就知道,r=n/(n/l)r = n / (n / l)r=n/(n/l),n/ln/ln/l便是这个块内的值。所以这一块对答案的贡献就是(r−l+1)∗(n/l)(r-l+1)*(n/l)(r−l+1)∗(n/l)
具体代码实现如下:
for(int l = 1 , r ; l <= x ; l = r + 1) {
r = x / (x / i );
ans += 1LL * (r - l + 1) * (x / l);
}
讲完了整除分块,我们回到原题。
题目是让我们求的数是l,rl,rl,r这个区间的,假设函数solve(x)solve(x)solve(x)表示[1,x][1,x][1,x]这个区间对应得答案,那么[l,r][l,r][l,r]区间的答案是不是就是
solve(r)−solve(l−1)!solve(r)-solve(l-1)!solve(r)−solve(l−1)!那我们只要把这个solve(x)solve(x)solve(x)写出来不就好啦!!!可以该怎么写呢?
因为题目要求我们求1−>91->91−>9出现的个数,所以我们可以一个一个的算,拿111举例,如果x%y==0x\%y == 0x%y==0
其中y∈[1,2),[10,20),[100,200),[1000,2000)y\in[1,2),[10,20),[100,200),[1000,2000)y∈[1,2),[10,20),[100,200),[1000,2000)那答案对1的贡献++,那我们就可以细化到每个区间(用l,rl,rl,r表示)。
那[1,x][1,x][1,x]区间有多少个数可以整除yyy呢?答案显然就是x/yx/yx/y;
那这一题就是让我们求(x/1)+(x/10+x/11+x/12+...)(x/1) + (x/10 + x/11 + x/12 + ...)(x/1)+(x/10+x/11+x/12+...)这不就是我们上面讲的整除分块的板子题了嘛。
AC代码如下:
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cmath>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define pii pair<int,int>
#define sd(x) scanf("%d",&x)
#define slld(x) scanf("%lld",&x)
#define pd(x) printf("%d\n",x)
#define plld(x) printf("%lld\n",x)
#define rep(i,a,b) for(int i = a ; i <= b ; i++)
#define per(i,a,b) for(int i = b ; i >= a ; i--)
#define mem(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define fast_io ios::sync_with_stdio(false)
const LL INF = 1e18;
const int mod = 2010;
const int maxn = 2e5 + 7;
LL solve(LL x,LL v) {
LL res = 0;
for(LL temp = 1 ; temp <= x / v ; temp *= 10) {
///确定左右区间
LL l = temp * v , r = min(x , l + temp - 1);
///整除分块
for(int i = l , j ; i <= r ; i = j + 1) {
j = min(x / (x / i) , r);
res += 1LL * (j - i + 1) * (x / i);
}
}
return res;
}
int main() {
int x,y;
while(~scanf("%d%d",&x,&y)) {
for(int i = 1 ; i <= 9 ; i++) {
///直接俄统计答案
printf("%lld\n" , solve(y,i) - solve(x-1,i));
}
}
return 0;
}