题目
思路
数位 d p dp dp 乱搞。考虑到各位数字之和的范围很小,枚举它,设为 r r r ,之后再做。
用 f ( i , j ) f(i,j) f(i,j) 表示各位数字之和目前为 i i i ,原数模 r r r 的结果是 j j j ,有多少个这样的数字。
统计答案用 f ( r , 0 ) f(r,0) f(r,0) 即可。似乎状态数有点多?
然后开始玄学优化。说实话,我讨厌这种乱优化(单纯的剪枝)的东西。出这种题真的很烦!
- 可行性剪枝(无意义状态):后方都是 9 9 9 也得不到 r r r ,就再见;已经超过了 r r r 也不行。
- 可行性剪枝(无意义转移):当前考虑了 x x x 个数位,则只有 i ≤ 9 x i\le 9x i≤9x 时 f ( i , j ) f(i,j) f(i,j) 可能不为零。
然后就过了。你说这种题是不是很烦?
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
inline int readint() {
int a = 0; char c = getchar(), f = 1;
for(; c<'0' or c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c and c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
void writeint(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar((x%10)^48);
}
# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
# define FOR(i,n) for(int i=0; i<(n); ++i)
typedef long long int_;
int_ dp[163][162][2], tmp[163][162][2];
void cls(int x,int r,int_ a[163][162][2]){ // 清空数组
for(int i=max(0,r-9*(19-x)); i<=9*x and i<=r; ++i)
FOR(j,r) FOR(k,2) a[i][j][k] = 0;
}
int d[19];
int_ work(int_ num){
for(int i=18; ~i; --i)
d[i] = num%10, num /= 10;
int_ res = 0;
for(int r=1; r<163; ++r){ // 数字之和
dp[0][0][0] = 1; // 常见初状态
for(int x=0; x<19; cls(x++,r,tmp)){
FOR(now,10) FOR(j,r) // 非常窘迫的剪枝
for(int i=max(0,r-9*(18-x)-now); i+now<=r and i<=9*x; ++i){
int_ *p = tmp[i+now][(j*10+now)%r];
if(now <= d[x])
p[now < d[x]] += dp[i][j][0];
p[1] += dp[i][j][1];
}
swap(dp,tmp); // 据说是O(1)的?
}
res += dp[r][0][1]; // 很稳!
cls(19,r,dp); // 清空好习惯
}
return res;
}
int main(){
int_ x, ans = 0;
cin >> x, ans -= work(x);
cin >> x, ans += work(x+1);
cout << ans << endl;
return 0;
}