2 条题解
-
2
注意到
按个位、十位等逐位单独算每位数字总和
用d记位权,c=n/(d*10) 算出完整周期数
每周期该位 0~9 累加和 45,
先累加45*d*c;再取余数 r=n%(d*10) 处理零散尾数
h=r/d是尾数段当前位最大值,
先补 0~h-1 的和 h(h-1)/2*d*,
再加 h 出现 r%d+1 次的贡献,
逐位累加得到 1~n 全部数位和。
当然位数DP也可以在这里我就不写了当然位数DP也可以在这里我就不写了当然位数DP也可以在这里我就不写了C++
ll n; int main() { AC; int t; cin>>t; while(t--) { cin>>n; if(n<=0) { cout<<0<<'\n'; continue; } ll sum=0; ll d=1; while(d<=n) { ll c=n/(d*10); sum+=45*d*c; ll r=n%(d*10); if(r>=d) { ll h=r/d; sum+=(h*(h-1)/2)*d; sum+=(r%d+1)*h; } d*=10; } cout<<sum<<'\n'; } return 0; }Python
import sys def main(): data=sys.stdin.read().split() t=int(data[0]) for n in map(int,data[1:t+1]): if n<=0: print(0) continue s=0 d=1 while d<=n: c=n//(d*10) s+=45*d*c r=n%(d*10) if r>=d: h=r//d s+=h*(h-1)//2*d s+=(r%d+1)*h d*=10 print(s) if __name__=="__main__": main() -
1
这个题的一个解法是数位dp
就是直接统计每个数位的出现频率再乘上本身
同时有一个类似的题
数位dp推荐都写成dfs形式的,因为递推式的数位dp是很难处理边界条件的
刚开始学数位dp我建议把状态全部记上,
比如常见的有第数位,前导零,上下界,
代码如下
#include <bits/stdc++.h> #define int long long using namespace std; int a[20], memo[16][16][2][2]; int dfs(int p,int sm,bool ld,bool lm,int tg) { if (p==0) { return sm; } // 这里的变量名含义,p是目前处理到第几个数位, // sm是当前该数位(也就是tg)一共出现了几次 // ld就是是否含有前导0 // lm就是是否贴着上界 //处理上下界的原因是因为枚举该位的数字 if (memo[p][sm][ld][lm]!=-1) { return memo[p][sm][ld][lm]; } int res=0; int up=lm ? a[p] : 9; //如果是27000,如果最高位不是2,下一位枚举的数字可以是0-9,但是如果最高位是2,下一位枚举的数字只能是0-7 for (int i=0;i<=up;i++) { int nsm=sm+ ((i==tg)&&!(ld && i==0)); //处理前导0的原因是因为数字不含前导0,比如9,如果有前导0计数可能是009,会干扰到0的计数 res+=dfs(p-1,nsm,ld && (i==0),lm && (i==up),tg); //这里如果下一位依然是0并且仍然处于前导0状态仍然记作前导0, // } return memo[p][sm][ld][lm]=res; //返回的时候直接把状态存上 } int sol(int n, int tg) { int len = 0; while (n) a[++len] = n % 10, n /= 10; memset(memo, -1, sizeof(memo)); return dfs(len,0,true,true,tg); } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin>>t; while(t--) { int n; cin>>n; int ans=0; for (int i=0;i<=9;i++) { ans+=sol(n,i)*i; } cout<<ans<<'\n'; } }然后恭喜你收到了一个TLE的代码
因为这个地方的 很大,并且 不受 的约束
所以数位dp时我们不能memset的方式来解决初始化
memset是因为我们我们在处理0-9每个数位以及每组数据之间都要重置一下,
我们可以直接把状态全部存完并且加上vis数组规避memset
#include <bits/stdc++.h> #define int long long using namespace std; int a[20]; int memo[20][20][2][2][10];//加上数位的状态 bool vis[20][20][2][2][10]; int dfs(int p, int sm, bool ld, bool lm, int tg) { if (p == 0) { return sm; } if (vis[p][sm][ld][lm][tg]) { return memo[p][sm][ld][lm][tg]; } int res = 0; int up = lm ? a[p] : 9; for (int i = 0; i <= up; i++) { int nsm = sm + ((i == tg) && !(ld && i == 0)); res += dfs(p - 1, nsm, ld && (i == 0), lm && (i == up), tg); } if (!lm) { vis[p][sm][ld][lm][tg] = true; return memo[p][sm][ld][lm][tg] = res; } return res; } int sol(int n, int tg) { int len = 0; while (n) a[++len] = n % 10, n /= 10; return dfs(len, 0, true, true, tg); } signed main() { ios::sync_with_stdio(false); cin.tie(nullptr); int t; cin >> t; while(t--) { int n; cin >> n; int ans = 0; for (int i = 0; i <= 9; i++) { ans += sol(n, i) * i; } cout << ans << '\n'; } return 0; }其实数位dp写多了你就能发现这东西其实是轮椅
- 1
信息
- ID
- 1166
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- (无)
- 递交数
- 22
- 已通过
- 5
- 上传者