数码

题目

给定两个整数l和r,对于任意x,满足l≤x≤r,把x所有约数写下来。
对于每个写下来的数,只保留最高位的那个数码。求[1,9]中每个数码出现的次数.
1≤l≤r≤10^9

解析:
如果正面去想每个数的约数,绝对超时,反过来如果a*b属于[l,r],那么a,b是符合条件的约数,

首先考虑约数i会被统计r/i-(l-1)/i次,鉴于其形式相同,考虑怎么统计r/i

分块

1、当约数i<sqrt®时只有sqrt®种情况,可以暴力统计

2、当约数i>=sqrt®时,约数r/i只有sqrt®种情况,可以把出现次数相同的约数统一统计

出现i次的约数是r/(i+1)+1到r/i,是连续的,那这些数有多少以1开头哪?其实可以统计该区间与{[1,1],[10,19],[100,199],[1000,1999]…}的重合部分的数量,这样就可以算了。

总复杂度O(sqrt(n)*log(n))

#include <iostream>
#include <cstdio>
#include <cmath>
 
typedef long long LL;
using namespace std;
 
LL cnt[11];
 
int f(int x) {
	while (x >= 10) x /= 10;
	return x; 
}
 //统计[l,r]区间中统计了v次,且第一位数字为tp的约数
void Add(LL l,LL r,int v,int tp) {
	LL opr = tp, opl = tp;
    while(opl <= r) {
    	if(opr >= l) cnt[tp] += v*(min(r,opr)-max(l,opl)+1LL);
    	opl = opl*10;
    	opr = opr*10+9;
	} 
}
 
void calc(int r,int cas) {
	int lim = (int)sqrt(r);	
	for (int i = 1; i <= lim; i++)//这个i代表的是统计相同次数的,
	  for (int j = 1; j <= 9; j++)//这j表示约数开头为j
	    Add(r/(i+1)+1,r/i,cas*i,j);
	for (int i = r/(lim+1); i >= 1; i--) cnt[f(i)] += cas*r/i;  
	//统计不在分组之内其他
}
 
int main() {
	int l,r; cin >> l >> r;
	calc(r,1); calc(l-1,-1);
	for (int i = 1; i <= 9; i++) printf("%lld\n",cnt[i]);
	return 0;
}

有纸质的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值