GDUT 练习题 [CodeForces-1513C] Add One

这篇博客介绍了如何解决一个数字进行多次加一操作后位数的问题。作者指出,直接的暴力模拟方法会导致时间复杂度过高,不适合大规模数据。通过观察和分析,作者提出了一种优化策略,即预计算0-9经过n次加一操作后的位数,并存储在一个数组中。利用这个数组,可以快速计算任意数字在给定操作次数后的位数,实现了时间复杂度为O(n)的解决方案。文章还给出了相应的C++代码实现。
摘要由CSDN通过智能技术生成

题目描述:

对一个数字进行每一位加一的操作,如果进位,则添加一位。例如9876进行一次操作就会变成10987,再来一次就是211098,以此类推。输入数字和操作次数,输出最后数字的位数。

有 T < 2e5 个测试用例,数字小于1e9,操作次数小于2e5

思路:

如果使用单纯的暴力模拟,时间复杂度为O( n ^ 2 ),数量级到了1e10,时间不支持,所以我们需要另寻办法,仔细思考我们可以发现,对一个数字来说,它进行了n次操作后得到的数字是一定的,不会受到其他的数字影响。而且操作次数的数量级也不是特别的大,我们就可以先将0~9进行N次操作后会达到多少位存进一个数组里面,然后将输入的数字的每一位最后得到的数位加起来取个模就可以了。

那我们应该算出对应的数组呢,首先,0~9的结果是肯定的,都是1位。然后我们发现,对于一个数字,只要进行10 - a 次操作之后必定会多出一位,变成10。然后出现的1和0又会在9次和10次之后再次变成10.那么我们就可以得到转换关系,每次出现进位的时候就将9位和10位后面的数做出标记,如果遇到有标记的地方就再次进行这个操作。这里用了桶来标记,实际上可以在同一个数组完成这个操作,只须先检测该位是否为0即可。(原本我用的队列,笑死,根本不行

时间复杂度为O( n )

代码:

#include <bits/stdc++.h>
#define MOD 1000000007
using namespace std;
int numplus[ 200010 ];
int saber[ 20 ];
int bucket[ 200100 ];

int main() {
	int T;
	char temp;
	int i, j, k, p;
	long ans;
	/*plusnum.push(10); 这里是原本想要用队列做发现不行
                        因为用队列无法保证队列里的序号是顺序的
                        用优先队列的话会超时
	numplus[ i ] = 1;
	for ( i = 1; i < 200010; i++ ) {
		numplus[ i ] = numplus[ i - 1 ];
	lable :
		if ( i == plusnum.front() ) {
			plusnum.push( i + 9 );
			plusnum.push( i + 10 );
			plusnum.pop();
			numplus[ i ] ++;
			numplus[ i ] %= MOD;
			goto lable;
		}
	}*/
	numplus[ 0 ] = 1;
	bucket[ 10 ] ++ ;
	for ( i = 1 ; i < 200010 ; i ++ ) {
		numplus[ i ] = numplus[ i - 1 ];
		if ( bucket[ i ] ) {
			bucket[ i + 9 ] += ( bucket[ i ] % MOD ) ;
			bucket[ i + 10 ] += ( bucket[ i ] % MOD );
			numplus[ i ] += ( bucket[ i ] % MOD );
			numplus[ i ] %= MOD;
		}
	}
	cin >> T;
	while ( T -- ) {
		ans = 0, p = 0;
		getchar();
		temp = getchar();
		while ( temp != ' ' ) {
			saber[ ++p ] = temp - '0';
			temp = getchar();
		}
		cin >> i;
		for ( j = 1 ; j <= p ; j++ ) {
			ans += numplus[ saber[ j ] + i ];
			ans %= MOD;
		}
		cout << ans << '\n';
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值