ZJOI2010 数字计数

题目大意:

给定a,b, 求 [a, b] 中所有整数中, 每个数字出现的次数。

输入格式:

输入两个整数 a 和 b。

输出格式:

输出 10 个整数, 分别表示 0, 1, 2, ....., 9  在 [a, b] 中出现了多少次。

样例:

输入:

1 99

输出:

9 20 20 20 20 20 20 20 20 20

数据范围: 

- 对于 30% 的数据,保证 a ≤ b ≤ 10^6;
- 对于 100% 的数据,保证 1 ≤ a ≤ b ≤ 10^12。
 

做法:

通过观察数据范围发现 a, b 非常大, 直接O(n)枚举会寄, 于是我们另辟蹊径, 可以发现求区间[a, b] 中所有整数, 每个数字出现的次数。 相当于求 [0, b] 每个数字出现的次数 减去 [0, a - 1] 每个数字出现的次数。

于是问题变成了求[0, b] 和 [0, a - 1] 中所有整数, 每个数字出现的次数。(跟没变一样。。。)

但这样还是会TLE啊。

再进一步想,求每个数字出现的次数就是统计每一位上每一个数字出现的次数

于是问题转换成枚举每一位然后统计。

那么我们来看一个栗子:1923

数字:1 9 2 3
下标:4 3 2 1

当我们统计位置 2 上每一个数字出现的个数是可以分两种情况考虑:

1.因为我们构造的数必须在 [0, 1923] 之中,所以当位置 2 前面的那一部分(即:位置3, 4)小于 1923 的前面那一部分(即:位置3, 4) 那么位置 2 可以填 0 到 9 中的任意一个数。那么每个数有多少个呢? 因为 1923 前面那一部分是 "19",我们构造的数要小于 "19" 那么只能填 0 到 18, 同时我们保证了前面一定小于 19 所以位置 2 后面的那一部分每一位都可以填0到9, 那么其总数就是 19 * 10^1

2.上一种情况我们只考虑了小于 1923 的数,这里我们还是针对位置 2 考虑前面那一部分(即:位置3, 4)等于 19 的情况, 可以发现对于位置2,可以填 0 到 2 中的所有数,不过这里注意  对于 0 到 1 后面每一位都可以填 0 到 9, 但是对于 2 我们后面只能填 0 到 3(不能超过1923)

那么对于数字 0 到 1 其总数就是 10^1

对于数字 2 其总数就是 4 (3 + 1)

总结下就是对于数字 xnx(n-1)x(n-2)...x1 指的是 x3 = 1, x2 = 2, x1 = 3 组成的数 123

1.位置 i 上 f[0...9] += xnx(n-1)x(n-2)...x(i+1) * 10^i-1

2.位置 i 上 f[0...xi - 1] += 10^i-1, f[xi] +=  x(i-1)x(i-2)...x1

注意:因为 0 前面不能为 0, 所以得注意一下(可以像我一样单独考虑0)

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int g[20], f[20];

int fast_pow(int a, int b){
	int res = 1;
	while(b){
		if(b & 1) res *= a;
		a *= a;
		b >>= 1;
	}
	return res;
}

signed main() {
//	freopen(".in", "r", stdin);
//	freopen(".out", "w", stdout);
	int a, b, alen = 0, blen = 0, a1, b1;
	cin >> a >> b;
	a1 = max(0ll, a - 1), b1 = b;
	while(a1){
		a1 /= 10;
		alen++;
	}
	while(b1){
		b1 /= 10;
		blen++;
	}
	int op = a;
	a = max(a - 1, 0ll);
	for(int i = 1; i <= alen; i++){//
		if(i < alen){
			for(int j = 1; j <= 9; j++){
				g[j] = g[j] + (a / fast_pow(10, i)) * fast_pow(10, i - 1);
			}
		}
		for(int j = 1; j <= (a / fast_pow(10, i - 1)) % 10; j++){
			if(j == ((a / fast_pow(10, i - 1)) % 10)) g[j] = g[j] + a - ((a / fast_pow(10, i - 1)) * fast_pow(10, i - 1)) + 1;
			else g[j] = g[j] + fast_pow(10, i - 1);
		}
		
	}
	for(int i = 1; i <= alen; i++){
		if(i < alen){
			g[0] = g[0] + (a / fast_pow(10, i) - 1) * fast_pow(10, i - 1);
		}
		if((a / fast_pow(10, i)) > 0){
			if(((a / fast_pow(10, i - 1)) % 10) == 0) g[0] = g[0] + a - ((a / fast_pow(10, i - 1)) * fast_pow(10, i - 1)) + 1;
			else g[0] = g[0] + fast_pow(10, i - 1);
		}
	}
	a = op;
	
	for(int i = 1; i <= blen; i++){
		if(i < blen){
			for(int j = 1; j <= 9; j++){
				f[j] = f[j] + (b / fast_pow(10, i)) * fast_pow(10, i - 1);
			}
		}
		for(int j = 1; j <= (b / fast_pow(10, i - 1)) % 10; j++){
			if(j == (b / fast_pow(10, i - 1)) % 10) f[j] = f[j] + b - ((b / fast_pow(10, i - 1)) * fast_pow(10, i - 1)) + 1;
			else f[j] = f[j] + fast_pow(10, i - 1);
		}
	}
	for(int i = 1; i <= blen; i++){
		if(i < blen){
			f[0] = f[0] + (b / fast_pow(10, i) - 1) * fast_pow(10, i - 1);
		}
		if((b / fast_pow(10, i)) > 0){
			if(((b / fast_pow(10, i - 1)) % 10) == 0) f[0] = f[0] + b - ((b / fast_pow(10, i - 1)) * fast_pow(10, i - 1)) + 1;
			else f[0] = f[0] + fast_pow(10, i - 1);
		}
	}
	for(int i = 0; i <= 9; i++){
		cout << f[i] - g[i] << " ";
	}
    return 0;
}//by hwl
  • 11
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
MATLAB中的科学计数法是一种表示较大或较小数字的方法。科学计数法的形式是a×10的n次幂,其中1≤|a|<10,n表示整数。这种表示方法可以方便地表示日常生活中遇到的一些较大或较小的数。例如,光的速度大约是300,000,000米/秒,可以用科学计数法表示为3×10^8米/秒。\[2\] 在MATLAB中,当数字的有效数字过多时,会使用科学计数法来表示。例如,532*20.3在科学计数法中表示为1.0800e+04,其中1.0800e+04等同于10799.6。这种表示方法可以简化较大或较小数字的书写和阅读。\[1\] 另外,MATLAB中的表达式也可以使用科学计数法。例如,-(1.0*(5.317e17*s^3+9.097e22*s^2+4.917e26*s+5.983e31)*(2.5063e-18*s^4+1.6461e-13*s^3+5.406e-9*s^2+0.00010396*s+1.0))/(s^7+176988.0*s^6+4.429e9*s^5+5.472e14*s^4+3.208e18*s^3+2.72e23*s^2+1.045e26*s+1.539e29)就是一个使用科学计数法表示的表达式。\[3\] #### 引用[.reference_title] - *1* [MATLAB中输出科学计数法_MATLAB中的常量与变量](https://blog.csdn.net/weixin_39565300/article/details/110303463)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [matlab不用科学计数法](https://blog.csdn.net/weixin_36002881/article/details/116506715)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值