背景
NOIP2010普及组复赛第一题加强版
描述
请统计某个给定范围[L, R]的所有整数中,数字2 出现的次数。
比如给定范围[2, 22],数字2 在数2 中出现了1 次,在数12 中出现1 次,在数20 中出
现1 次,在数21 中出现1 次,在数22 中出现2 次,所以数字2 在该范围内一共出现了6
次。
比如给定范围[2, 22],数字2 在数2 中出现了1 次,在数12 中出现1 次,在数20 中出
现1 次,在数21 中出现1 次,在数22 中出现2 次,所以数字2 在该范围内一共出现了6
次。
输入格式
输入共1 行,为两个正整数L 和R,之间用一个空格隔开。
输出格式
输出共1 行,表示数字2 出现的次数。
测试样例1
输入
【输入样例1】
2 22
【输入样例2】
2 100
输出
输出样例1:
6
输出样例2:
20
备注
【数据范围】
0 ≤ L ≤ R≤ 10^10。NOIP2010普及组复赛——NO.1
0 ≤ L ≤ R≤ 10^10。NOIP2010普及组复赛——NO.1
原本想写一个dp的代码,结果发现写成了模拟
一定范围的数里有多少个2是固定的比如说0-9,0-99,0-999这样的几位数中的2的个数都可以递推出来(其实hxy用的打标也可以)
然后就可以把数的每一位分解,然后计算出0-这一位代表的数中有多少个2,然后相加
要注意的是这个数中本身就有2的情况。如果这一位是2,那么往后所有的数都会多出一个2
求出0-l,0-r中2的个数,然后相减即可。注意如果l里本身就有2的话需要再加上2的数量
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
long long l,r,n,m,ans;
long long mi[20],f[20];
inline int doit(int now){
long long k=now,weishu=0,tot=0,num=0,ans=0;
while (k>0){
num=k%10;
weishu++;
if (num==0){
k/=10; continue;
}
tot=f[weishu];
if (num>2) tot-=(10-num)*f[weishu-1];
else if (num<=2){
tot=num*f[weishu-1];
if (num==2) tot++;
}
ans+=tot;
k/=10;
}
return ans;
}
int main(){
scanf("%lld%lld",&l,&r);
mi[0]=1;
for (int i=1;i<=10;++i) mi[i]=mi[i-1]*10;
f[1]=2;
for (int i=1;i<=10;++i)
f[i]=f[i-1]*10+mi[i-1];
n=doit(l); long long l1=l;
long long x=0,y=0,z=1;
while (l>0){
x=l%10;
if (x==2) n+=y;
y=x*z+y;
z*=10;
l/=10;
}
m=doit(r);
x=0,y=0,z=1;
while (r>0){
x=r%10;
if (x==2) m+=y;
y=x*z+y;
z*=10;
r/=10;
}
ans=m-n;
l=l1;
while (l>0){
int x=l%10;
if (x==2) ans++;
l/=10;
}
printf("%lld\n",ans);
}