码迷,mamicode.com
首页 > 其他好文 > 详细

UVA11361 Investigating Div-Sum Property

时间:2021-04-26 13:29:15      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:个数   read   clu   splay   set   har   etc   枚举   property   

UVA11361 Investigating Div-Sum Property

给定 \(A,B,K\),求在 \([A , B]\) 中,有多少个整数本身能被 \(K\) 整除,并且各个位上数字之和也能被 \(K\) 整除。

一道非常经典的数位 DP 题。

\(f(d,m_1,m_2)\) 表示共有 \(d\) 位待填,且这 \(d\) 位的各位数字之和模 \(K\)\(m_1\),所组成的数字模 \(K\)\(m_2\)

显然:

\[f(d,m_1,m_2)=\sum_{x=0}^9 f(d-1,(m_1-x)\mod k,(m_2-x\times 10^{d-1})\mod k) \]

然后稍微总结一下数位 DP 的套路:

int dfs(状态){
    if(最后一位){return ...;}
    if(已经搜过) return f[状态];
    对这一位的单独处理 / 特殊限制。
    for(下一位)
        now += dfs(下一个状态);
    return f[状态] = now;
}

int work(int x){
    或许有的特判 + 预处理
	for(枚举第一个状态)
        ans += dfs(记忆化搜索);
    return ans;
}

int main(){
    ans = work(b) - work(a - 1);
}

主要难点事状态的设计。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 20;
const int M = 200;
int T, a, b, k, num[N];
LL p[N], f[N][M][M];

int read(){
    int x = 0, f = 1; char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) f = (c == ‘-‘) ? -1 : 1, c = getchar();
    while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - 48, c = getchar();
    return x * f;
}

LL dfs(int d, int m1, int m2){
    if(!d) return (!m1 && !m2);
    if(f[d][m1][m2] != -1) return f[d][m1][m2];
    LL now = 0;
    for(int i = 0; i <= 9; i ++)
        now += dfs(d - 1, ((m1 - i) % k + k) % k, ((1LL * m2 - 1LL * i * p[d - 1]) % k + k) % k);
    return f[d][m1][m2] = now;
}

LL work(LL x){
    if(!x) return 1LL;
    // 根据后面的处理方式,防止漏掉 x 这个数没有判断。
    x ++;
    int cnt = 0;
    for(int i = x; i; i /= 10) num[++ cnt] =  i % 10;
    if(cnt * 9 < k) return 1LL;
    LL ans = 0;
    int tot1 = 0, tot2 = 0;
    for(int i = cnt; i >= 1; i --){
        // 枚举前 i 位的数字填什么。
        for(int j = 0; j < num[i]; j ++){
            if(j){
                tot1 = (tot1 + 1) % k;
                tot2 = (tot2 + p[i - 1]) % k;
            }
            ans += dfs(i - 1, ((k - tot1) % k + k) % k, ((k - tot2) % k + k) % k);
        }
        // 第 i 位最后确定为 num[i],所以之前 0 ~ i-1 时后面几位没有限制。
        if(num[i]){
            tot1 = (tot1 + 1) % k;
            tot2 = (tot2 + p[i - 1]) % k;
        }
    }
    return ans;
}

int main(){
    T = read();
    p[0] = 1;
    for(int i = 1; i <= 15; i ++) p[i] = p[i - 1] * 10;
    while(T --){
        memset(f, -1, sizeof(f));
        a = read(), b = read(), k = read();
        printf("%lld\n", work(b) - work(a - 1));
    }
    return 0;
}

UVA11361 Investigating Div-Sum Property

标签:个数   read   clu   splay   set   har   etc   枚举   property   

原文地址:https://www.cnblogs.com/lpf-666/p/14698744.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!