题目链接:https://ac.nowcoder.com/acm/problem/13221
题意:给定两个整数 l l l和 r r r ,对于所有满足 1 ≤ l ≤ x ≤ r ≤ 1 0 9 1 ≤ l ≤ x ≤ r ≤ 10^9 1≤l≤x≤r≤109的 x x x ,把 x x x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求 1 ~ 9 1~9 1~9每个数码出现的次数。
思路:
在讲这一题得写法之前,我们先要引入整除分块这个知识。
整除分块是求 ∑ i = 1 n n / i \sum_{i=1}^{n}n/i ∑i=1nn/i的和。
首先如果我们直接 f o r for for循环遍历一遍,那一般题目一定会超时。还是遵循遇事不决先打表的原则,我们将 n = 10 n=10 n=10带进去模拟一下,可以得到如下表所示的值:
我们可看到有很多的之都是一样的,那我们是不是就可以将所有 n / i n/i n/i相等的放在“一块”呢?那我们可以定义两个变量 l , r l,r l,r,用 l l l表示这一块的左区间, r r r表示这一块的右区间,所以 l l l我们要从一开始遍历,每次令 l = r + 1 l = r + 1 l=r+1,那 r r r呢?我们在草稿纸上算一下就知道, r = n / ( n / l ) r = n / (n / l) r=n/(n/l), n / l n/l n/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 , r l,r l,r这个区间的,假设函数 s o l v e ( x ) solve(x) solve(x)表示 [ 1 , x ] [1,x] [1,x]这个区间对应得答案,那么 [ l , r ] [l,r] [l,r]区间的答案是不是就是
s o l v e ( r ) − s o l v e ( l − 1 ) ! solve(r)-solve(l-1)! solve(r)−solve(l−1)!那我们只要把这个 s o l v e ( x ) solve(x) solve(x)写出来不就好啦!!!可以该怎么写呢?
因为题目要求我们求 1 − > 9 1->9 1−>9出现的个数,所以我们可以一个一个的算,拿 1 1 1举例,如果 x % y = = 0 x\%y == 0 x%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 , r l,r l,r表示)。
那 [ 1 , x ] [1,x] [1,x]区间有多少个数可以整除 y y y呢?答案显然就是 x / y x/y x/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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define pii pair
#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;
}