洛谷 P4999 烦人的数学作业
题目链接
这道题从下午一来就开始写,一直写到\(4:00\),终于写完了,累死我辣,但是做出来的感觉还是很苏服的~。
本题思路:按位考虑+模拟
题目大意是很好理解的,就是算从l到r的所有数中每个数的每一位数字加起来的和。
如果你从\(l\)到\(r\)这样一个个枚举的话,就是暴力了,不知道有没有分……
思路分析:
既然是模拟的话,就需要对这个所求有深入的理解。
- 可以把\(l\)到\(r\)的答案转化成\(0~r\)的答案减去\(0~l-1\)的答案。(一个类似前缀和的思想)
- 下面只要考虑\(0\)到\(x\)的答案怎么求了,我在这里说一下我按位处理的过程,代码实现很简单:
- 对于一个数字\(x\),我们定义它的位数是\(tot\),每一位上的数字是\(a[i]\)。
- 考虑最高位:在\(0~x\)的数中,最高位的数可以是\(1~a[i]\)。
- 其中\(1~a[1]-1\)出现了\(10^{tot-1}\)次,因为可以默认最高位是\(1~a[1]-1\)中的某一个数,然后后面\(tot-1\)位可以任取\(0~9\),一共是\(10^{tot-1}\)个数\((0~999……)\),所以答案加上\(\sum\limits_{j=1}^{j<a[1]} j*(10^{tot-1})\)。
- \(a[i]\)出现了\(x-a[i]*10^{tot-1}+1\)次(就是x去掉最高位剩下的数+1),就是默认以它为最高位的时候,后面\(tot-1\)位可以是\(0\)到\(x-a[i]*10^{tot-1}\),答案加上
\(a[i]*(x-a[i]*10^{tot-1}+1)\) - 举个例子:当\(x=4321\)的时候,最高位是\(4\),这一位对答案的贡献就是\(1*1000+2*1000+3*1000+4*322\),分别对应\(1000~1999,2000~2999,3000~3999,4000~4321\)这些数最高位对答案的贡献。
- 考虑最低位:最低位的数可以是\(1~9\)。
- 其中1~a[tot]这些数出现了\(\Large \lfloor\frac{x}{10}\rfloor\)\(+1\)次(就是\(x\)去掉最低位剩下的数\(+1\)),\(a[tot]+1~9\)这些数出现了\(\Large \lfloor\frac{x}{10}\rfloor\)次
- 所以最后一位的贡献就是\(\sum\limits_{i=1}^{i<=a[tot]}i*(\)\(\Large \lfloor\frac{x}{10}\rfloor\)\(+1)+\sum\limits_{i=a[tot]+1}^{i<=9}i*\)\(\Large \lfloor\frac{x}{10}\rfloor\)
- 举个例子:\(1234\)中最低位的贡献就是\(1*124+2*124+3*124+4*124+5*123+6*123+7*123+8*123+9*123\),分别对应\(0001~1231,0002~1232,0003~1233,0004~1234,0005~1225,0006~1226,0007~1227,0008~1228,0009~1229\)(这里的\(~\)是指前三位的变化,\(0001~1231\)指的是第四位是\(1\),前三位是\((000、001……123)\)的那些数)
- 剩下的其他位计算方法都一样:
- 对于某一位\(i\),它的答案分为三部分考虑:
- 若这一位是\(1~a[i]-1\),它的贡献为\(x\)的前\(i-1\)位所拼成的数加\(1\)再乘上\(10^{tot-i}\)
- 若这一位就是\(a[i]\),它的贡献就是\(x\)去掉\(i\)这一位后的数再\(+1\)
- \(a[i]+1~9\)的部分,它的贡献就是\(x\)的前\(i-1\)位所拼成的数再乘上\(10^{tot-i}\)
- 举个例子:当\(x=1234\)时,我们考虑第二位和第三位的贡献:
- \(i=2\),\(a[i]=2\)
- \(1~a[i]-1\)的数只有\(1\),所以贡献为前\(i-1\)位,即\(1\)加上\(1\)再乘上\(100\),贡献\(200\),表示第1位为0或1第3、4位为\(00~99\)的时候的贡献。
- \(a[i]=2\),贡献是\(x\)剩下的位\(134\)再\(+1\),贡献\(135\),表示第1、3、4位为\(000~134\)时候的贡献。
- \(a[i]+1~9\)的数是\(3~9\),每一个数贡献是这个数乘以前\(i-1\)位,即\(1\),再乘上\(100\),贡献\(300+400+500+600+700+800+900\),分别对应着第\(1\)位为\(0\)第\(3、4\)位为\(00~99\)的时候的贡献
- \(i=3\),\(a[i]=3\)
- \(1~a[i]-1\)的数是\(1、2\),所以贡献为前\(i-1\)位,即\(12\)加上\(1\)再乘上\(10\),贡献\(130\),表示第1、2位和起来为00~12,第4位为\(0~9\)的时候的贡献。
- \(a[i]=2\),贡献是\(x\)剩下的位\(124\)再\(+1\),贡献\(125\),表示第1、2、4位为\(000~124\)时候的贡献。
- \(a[i]+1~9\)的数是\(4~9\),每一个数贡献是这个数乘以前\(i-1\)位,即\(12\),再乘上\(10\),贡献\(12*40+12*50+12*60+12*70+12*80+12*90\),分别对应着第\(1、2\)位为\(0~11\),第\(4\)位为\(0~9\)的时候时贡献
然后就没了
码:
- \(i=2\),\(a[i]=2\)
- 对于某一位\(i\),它的答案分为三部分考虑:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=110;
const LL mod=1e9+7;
LL cnt[maxn],a[maxn];
LL getcnt(LL i){
LL ans=0;
while(i){
ans++;
i/=10;
}
return ans;
}
LL mypow(LL a,LL b){
LL ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
LL shu(LL i){
LL ans=0;
LL tot=0;
while(i){
cnt[++tot]+=i%10;
ans+=i%10;
i/=10;
}
return ans;
}
LL suan(LL qian,LL hou,LL res1,LL resn,LL tot){
LL ans=0,nowans=0;
ans+=res1*(hou+1);
for(int i=1;i<=9;i++){
if(i<res1)nowans+=i;
if(i<=resn)ans+=i*(qian+1);
if(i>resn)ans+=i*qian;
}
ans+=nowans*mypow(10,tot-1);
return ans;
}
LL clac(LL n){
if(n==0)return 0;
memset(a,0,sizeof(a));
LL tot=getcnt(n);
LL qian=0,hou=0,zhong=0,ans=0;
for(int i=tot;i>=1;i--){
a[i]=n%10;
n/=10;
}
for(int i=1;i<=tot;i++){
if(i>1)hou=(hou*10+a[i])%mod;
if(i<tot)qian=(qian*10+a[i])%mod;
}
ans+=suan(qian,hou,a[1],a[tot],tot);
for(int i=2;i<tot;i++){
qian=0;hou=0;
for(LL j=1;j<a[i];j++)qian+=j;
for(LL j=a[i]+1;j<=9;j++)hou+=j;
LL now=0;
for(int j=1;j<=tot;j++)if(i!=j)now=(now*10+a[j])%mod;
ans=(ans+a[i]*(now+1)%mod)%mod;
now=0;
for(int j=1;j<i;j++){
now=(now*10+a[j])%mod;
}
ans=(ans+qian*(now+1)%mod*mypow(10,tot-i)%mod)%mod;
ans=(ans+hou*now%mod*mypow(10,tot-i)%mod)%mod;
}
return ans;
}
LL n;
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
LL l,r;
scanf("%lld%lld",&l,&r);
printf("%lld\n",(clac(r)-clac(l-1)+mod)%mod);
}
return 0;
}