今天这个题目的解决方案很简洁到惊人。但是使用这种方案解决此题需要一定的数学知识。话不多说,上题目。
这是2019年网易的编程题。
题目描述
小Q得到一个神奇的数列: 1, 12, 123,...12345678910,1234567891011...。
并且小Q对于能否被3整除这个性质很感兴趣。
小Q现在希望你能帮他计算一下从数列的第l个到第r个(包含端点)有多少个数可以被3整除。
输入描述:
输入包括两个整数l和r(1 <= l <= r <= 1e9), 表示要求解的区间两端。
输出描述:
输出一个整数, 表示区间内能被3整除的数字个数。
示例1
输入
2 5
输出
3
说明
12, 123, 1234, 12345...
其中12, 123, 12345能被3整除。
1.分析题目
这个“神奇的数列”的排列规律是:第n个数的大小是在纸上从左到右依次写下从1到n所形成的整数。
题目要求给出两个数字,一个是起始位置,一个是结束位置。求出这个数列的两个位置之间(包含两端点)能够被3整除的数。
下面开始寻找这个数列中的规律。
假设这个数列中第i个数为a[i](i>=2,i为整数),第i个数对3取余为last[i]。可以得到a[i]与a[i-1]的关系。
a[i]=a[i-1]*10^k+i;
k为i的十进制位数。显然可以看出。
Last[i]
=a[i]%3
=(a[i-1] *10^k+i)%3
=(a[i-1] *(10^k-1)+ a[i-1]+i)%3 //由这一步可以知道n*10^k%3=n%3
=(a[i-1]+i)%3 //k>=1,(10^k-1)总是能够被3整除的
显然可以上式也等于
=(a[i-1] %3+i%3)%3 //两个和对3取余等于分别对3取余,余数相加再对3取余。
即推论1:
last[i]= (last[i-1]+i%3)%3;
数学归纳法论证last[i]的规律。
当n = 0的时候:
last[0] = 0、last[1] = 1、last[2] = 0成立
假设last[3n]=0, last[3n+1]=1, last[3n+2]=0。
讨论last[3(n+1)]=( last[3n+2]+ 3(n+1)%3)%3=(0+0)%3=0
last[3(n+1)+1]= ( last[3(n+1)]+ (3(n+1)+1)%3)%3=(0+1)%3=1
last[3(n+1)+2]= ( last[3(n+1)+1]+ (3(n+1)+2)%3)%3=(1+2)%3=0
可证:
当n>=1时,
last[3n]=0, last[3n+1]=1, last[3n+2]=0成立。
可见当i%3=1时,last[i]=1。
即,在该数列中,连续每三个数中存在一个不能被3整除的元素。
位置n之前(包括n)不能被3整除的元素的个数为(n+2)/3。
神奇的数学归纳法!
这样要求输入数据l,r两个位置之间有多少个能够被3整除的数就好计算了。
从l到r一共有l-r+1个元素。
r之前包括r在内的元素中有(r+2)/3个不能被3整除。
l-1之前包括r在内的元素中有(l-1+2)/3个不能被3整除。
二者相减即为l到r之间不能能被3整除的元素个数。(r+2)/3-(l-1+2)/3
l到r之间能被3整除的元素个数为l-r+1-(r+2)/3+(l-1+2)/3
源代码如下:
#include<stdio.h>
int main()
{
int l, r;
while(EOF!=scanf("%d %d", &l, &r))
printf("%d\n", r-l+1-(r+2)/3+(l-1+2)/3);
return 0;
}