题目大意:
def f(x):
int f ( int x ) {
if ( x == 0 ) return 0;
return f ( x / 10 ) + x % 10;
}
给出这么个函数,求[L, R]之间有多少个x满足x % f(x) == 0的
分析:
首先,这题的区间范围比较大,直接枚举必然TLE。(至于那种打表暴力搞过去的就不说,反正我个人是从来没这种水过去的命..T_T)
然后一个很明显的想法就是用部分合的思想,ans = gao(R) - gao(L-1),这是一个常用的做法,关键就在于如何设计这个gao()函数了
通常这种题常用的做法就是数位统计DP(当时青椒讲数位统计的时候没好好研究,悲催的搞了我好久)
经常观察可知f(x) <= 81, 设dp[len][mod][sum][res]表示长度为len,%mod, 和为sum, 余数为res的个数
根据同余定理,从后面填一个数字x,可得到状态转移方程 dp[len+1][mod][sum+x][(res*10 + x) % mod] += dp[len][mod][sum][res]
预处理的部分其实很简单,难点在于如何利用这个DP值统计[1...X] 的个数
从高位开始统计,假如当前位为第i位,当前位的数字为a_i, mod, 剩下数字和为s, 那么我们可取的数字j为0 ~ a_i - 1, 则枚举剩下可能的余数res
if num + j * 10^(i-1) + res % mod == 0 则把dp[i-1][mod][s - j][res] 加到ans里面,然后剩下最低位的时候单独处理一下就是了。
int dp[11][83][83][83], ten[11];
void init() {
ten[0] = 1;
FOR(i, 1, 10) ten[i] = ten[i-1] * 10;
memset(dp, 0, sizeof(dp));
REP(i, 10)
FOR(j, 1, 82) dp[1][j][i][i%j] = 1;
FOR(i, 1, 9)
FOR(j, 1, 82)
REP(x, i*9+1)
REP(y, j+1)
if (dp[i][j][x][y]) {
for (int k = 0; k < 10 && x + k < 82; k++) {
dp[i+1][j][x+k][(y*10+k)%j] += dp[i][j][x][y];
}
}
return;
}
int a[12];
int calc(int x) {
if (x <= 10) return x;
int n = 0, s = 0;
for (int tmp = x; tmp; tmp /= 10, n++) s += tmp % 10, a[n+1] = tmp % 10;
int ret = 0;
int sum, num;
for (int mod = 1; mod <= 9 * n; mod++) {
if (mod > x) break;
sum = mod;
num = 0;
for (int i = n; i > 1; i--) {
for (int j = 0; j < a[i] && j <= sum; j++) {
for (int res = 0; res < mod; res++) {
if ((num + j * ten[i-1] + res) % mod == 0)
ret += dp[i-1][mod][sum - j][res];
}
}
sum -= a[i];
num += a[i] * ten[i-1];
}
}
while (1) {
if (x % s == 0) ret++;
if (x % 10 == 0) break;
x--; s--;
}
return ret;
}
int main() {
init();
int a, b, T, ca = 0;
sf("%d", &T);
while (T--) {
sf("%d%d", &a, &b);
pf("Case %d: %d\n", ++ca, calc(b) - calc(a - 1));
}
return 0;
}