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;
}

posted @ 2026-01-19 15:52  曾翎一  阅读(0)  评论(0)    收藏  举报