1-n数位和
我们以 n=7264 为例。
因为我们的任务是求 1−n 的数码之和,考虑对问题进行拆解:
求出 1−6999 的数码之和
求出 7000−7264 的数码之和
为什么这样拆?看下去就知道了。
对于第一部分:我们可以进行DP
约定 f(i,j) 为 i 位数,其中最高位是 j 的, 1−
j99...99
(共 i 位)数码之和。
比如例子中的 1−6999 就对应着 f(4,6) 。
那么我们有: f(i,j)=f(i,j−1)+(f(i,0)+10
i−1
×j),其中 j>0
比如 1−6999 数码和可以拆分成 1−5999 和 6000−6999 ,而 6000−6999 的数码和等于 1−999 的数码和与 1000 个 6 的和。
j=0 时,有 f(i,j)=f(i−1,9)
至此,我们解决了第一部分,主要的问题已经解决。
而 7000−7264 的部分我们把最高位 7 的贡献算上,然后问题就转化成了求 1−264 的数码之和。
这意味着我们的问题从 n=7624 变成了n=264 相当于少了一位,那么类似地继续下去,就可以求出答案了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=15;
ll f[N][10];
ll fpow(ll x,ll p){
ll res=1;
for(;p;p>>=1,x=x*x) if(p&1) res=res*x;
return res;
}
void init(){
for(int i=1;i<=N-1;i++)
for(int j=0;j<=9;j++){
if(j==0){
f[i][j]=f[i-1][9];
}else{
f[i][j]=f[i][j-1]+(f[i][0]+fpow(10,i-1)*j);
}
}
}
ll dp(int n){
ll res=0;
vector<int> v;
int num=n;
while(num) v.push_back(num%10),num/=10;
for(int i=v.size()-1;~i;i--){
int x=v[i];
if(x==0) continue;
res+=f[i+1][x-1];
res+=(n%fpow(10,i)+1)*x;
}
return res;
}
int main(){
init();
int n;
cin>>n;
cout<<dp(n)<<endl;
return 0;
}

浙公网安备 33010602011771号