数位DP

CF204A

题目链接
https://codeforces.com/problemset/problem/204/A
题目大意
image
模板讲解
image
image

数位DP模板

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n, m, t;
ll l,r;
string s;
ll memo[20][10][10];
ll dfs(int i, int first,int last,bool is_limit, bool is_num)
{
    if(i == m)
    {
        if(is_num && first == last) return 1;
        else                        return 0;
    }
    if(!is_limit && is_num && memo[i][first][last] != -1) return memo[i][first][last];
    ll res = 0;
    int up = is_limit ? s[i] - '0' : 9;
    if(!is_num) {
        res = dfs(i + 1,0,0,false,false);
        for(int d = 1;d <= up;++d){
            res += dfs(i + 1,d,d,is_limit && d == up,true);
        }
    }else{
        for (int d = 0; d <= up; ++d)
            res += dfs(i + 1,first, d,is_limit && d == up, true);
    }
    if (!is_limit && is_num)
        memo[i][first][last] = res;
    return res;
}
void solve() {
    cin >> l >> r;

    memset(memo,-1,sizeof(memo));
    s = to_string(l - 1);
    m = s.size();
    ll ans1 = dfs(0,0,0,true,false);

    memset(memo,-1,sizeof(memo));
    s = to_string(r);
    m = s.size();
    ll ans2 = dfs(0,0,0,true,false);
    cout << ans2 - ans1 << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    t = 1;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

CF1143B

题目链接
https://codeforces.com/problemset/problem/1143/B
题目大意
image
题目思路
dfs(i)表示值为i的乘积最大值。
不要被模板束缚,思路才是关键,状态转移方程要写出来!
题目代码

#include<bits/stdc++.h>
using namespace std;
int n, t;
int dfs(int x){
    if(x < 10) return max(1,x);
    return max(x % 10 * dfs(x / 10),9 * dfs(x / 10 - 1));
}
void solve() {
    cin >> n;
    cout << dfs(n) << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    t = 1;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

CF914C

题目链接
https://codeforces.com/problemset/problem/914/C
题目大意
image
坑点
x = 1的时候,操作数为0,所以要特判!
k = 0的时候,只有1满足,所以要特判!
代码

#include<bits/stdc++.h>

using namespace std;
const int MOD = 1e9 + 7;
int n,m,t;
string s;
int one[1005],cnt[1005];
int memo[1005][1005];
int dfs(int i, int ones,bool is_limit, bool is_num)
{
    if(i == m)
    {
        if(is_num && cnt[ones] + 1 == n) return 1;
        else                             return 0;
    }
    if(!is_limit && is_num && memo[i][ones] != -1) return memo[i][ones];
    int res = 0;
    int up = is_limit ? s[i] - '0' : 1;
    if(!is_num) {
        res = dfs(i + 1,0,false,false) % MOD;
        res = (res + dfs(i + 1,1,is_limit,true)) % MOD;
    }else{
        for (int d = 0; d <= up; ++d)
            res = (res + dfs(i + 1,ones + (d == 1),is_limit && d == up, true)) % MOD;
    }
    if (!is_limit && is_num)
        memo[i][ones] = res;
    return res;
}
void solve(){
    for(int i = 1;i <= 1000;++i){
        one[i] = one[i ^ (i & -i)] + 1;
    }
    for(int i = 2;i <= 1000;++i){
        cnt[i] = cnt[one[i]] + 1;
    }
    memset(memo,-1,sizeof(memo));
    cin >> s;cin >> n;
    if(n == 0) cout << 1 << '\n';
    else{
        m = s.size();
        int ans = dfs(0,0,true,false);
        if(n == 1) cout << ans - 1 << '\n';
        else       cout << ans << '\n';
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    t = 1;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

CF55D

题目链接
https://codeforces.com/problemset/problem/55/D
题目大意
image
思路
这题的思路很好想,主要是数学优化
代码版本1

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5 + 5,M = 2520;
int n, m, t,idx;
ll memo[20][50][M + 5];
ll l,r;
unordered_map<int,int>mp;
string s;
ll dfs(int i,int mp_lcm,int lcm,ll sum,bool is_limit,bool is_num){
    if(i < 0){

        if(is_num && sum % lcm == 0) return 1;
        else                         return 0;
    }
    if(!is_limit && is_num && ~memo[i][mp_lcm][sum % M]) return memo[i][mp_lcm][sum % M];
    ll ans = 0;
    int up = is_limit ? s[i] - '0' :9;
    if(!is_num){
        ans = dfs(i - 1,0,0,0,false,false);
        for(int d = 1;d <= up;++d){
            if(!mp.count(d)) mp[d] = idx++;
            ans += dfs(i - 1,mp[d],d,d,is_limit && d == up,true);
        }
    }else{
        for(int d = 0;d <= up;++d){
            if(!mp.count(lcm * d / gcd(lcm,d)))  mp[lcm * d / gcd(lcm,d)] = idx++;
            ans += dfs(i - 1, d == 0 ?mp[lcm] :mp[lcm * d / gcd(lcm,d)],d == 0 ?lcm :lcm * d / gcd(lcm,d),sum * 10 + d,is_limit && d == up,true);
        }
    }
    if(!is_limit && is_num){
        memo[i][mp_lcm][sum % M] = ans;
    }
    return ans;
}
void solve() {
    cin >> l >> r;
    s = to_string(r);
    m = s.size();
    reverse(s.begin(),s.end());
    ll ans = dfs(m - 1,0,0,0,true,false);

    s = to_string(l - 1);
    m = s.size();
    reverse(s.begin(),s.end());
    cout << ans - dfs(m - 1,0,0,0,true,false)  << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    memset(memo,-1,sizeof(memo));
    cin >> t;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

代码版本2

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 20,M = 252;
int n, m, t,idx;
int LCM[10][M * 10 + 5],h[10][M + 5];
ll memo[N][50][M + 5];
ll l,r;
unordered_map<int,int>mp;
int s[N];
int gcd(int x,int y){ 
     return y?gcd(y,x%y):x; 
}
int f(ll x){
	int i = 0;
	while(x){
		s[i++] = x % 10;
		x /= 10;		
	}
	return i;
} 
ll dfs(int i,int lcm,ll sum,bool is_limit){
    if(i < 0) return sum % lcm == 0;
    if(!is_limit && ~memo[i][mp[lcm]][sum]) return memo[i][mp[lcm]][sum];
    ll ans = 0;
    int up = is_limit ? s[i] : 9;
    for(int d = 0;d <= up;++d){
        ans += dfs(i - 1,LCM[d][lcm],i ? h[d][sum]:sum * 10 + d,is_limit && d == up);
    }
    if(!is_limit){
        memo[i][mp[lcm]][sum] = ans;
    }
    return ans;
}
void solve() {
    cin >> l >> r;
    m = f(r);
    ll ans = dfs(m - 1,1,0,true);

    m = f(l - 1);
    cout << ans - dfs(m - 1,1,0,true)  << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    memset(memo,-1,sizeof(memo));
    for(int i = 0;i < 10;++i){
        for(int j = 1;j <= 2520;++j){
            LCM[i][j] = i ? i * j / gcd(i,j) : j;
        }
        for(int j = 0;j < 252;++j){
        	h[i][j] = (j * 10 + i) % 252;
		}
    }
    for(int i = 1;i <= 2520;++i){
        if(2520 % i == 0) mp[i] = idx++;
    }
    cin >> t;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

注意点
①i从m - 1到0递减,为何?【可以复用memo里的元素】;
②1 - 9的最小公倍数为2520!
③当涉及到重复计算的变量时,可以先预处理,提高效率!
④对于数位组成的数,在最后一次递归之前,只需要对252取余即可,为何,因为最后一次还要乘10,那么最后一次就相当于对2520取余了,因为只要它满足是lcm的倍数,对2520取余后也一定是lcm的倍数!

CF628D

题目链接
https://codeforces.com/problemset/problem/628/D
题目大意
image
思路
求出[a + 1,b] 的方案数,然后再单独判断a,即可! f(b) - f(a) + judge(a)
代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e3 + 5,MOD = 1e9 + 7;
int n,m,d,t;
string a,b;
string s;
ll memo[N][N][2];
ll dfs(int i,int pre,bool even,bool is_limit){
    if(i == -1) return pre % n == 0;
    if(!is_limit && ~memo[i][pre][even]) return memo[i][pre][even];
    ll res = 0;
    int up = is_limit ? s[i] - '0' : 9;
    for(int j = 0;j <= up;++j){
        if(even && j == d || (!even && j != d))
            res = (res + dfs(i - 1,(pre * 10 + j) % n,!even,is_limit && j == up)) % MOD;
    }
    if(!is_limit) memo[i][pre][even] = res;
    return res;
}
void solve() {
    memset(memo,-1,sizeof(memo));
    cin >> n >> d;cin >> a >> b;m = a.size();
    reverse(b.begin(),b.end());
    s = b;
    ll ans = dfs(m - 1,0,false,true);
    reverse(a.begin(),a.end());
    s = a;
    ans = (ans - dfs(m - 1,0,false,true) + MOD) % MOD;
    int sum = 0,i;
    bool even = false;
    for(i = m - 1;i >= 0;--i){
        int d1 = a[i] - '0';
        sum = (sum * 10 + d1) % n;
        if((even && d1 == d)|| (!even && d1 != d)) even = !even;
        else                                       break;
    }
    if(i == -1 && sum == 0) ans = (ans + 1) % MOD;
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    t = 1;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

CF507D

题目链接
https://codeforces.com/problemset/problem/507/D
题目大意
image
题目思路
注意,有n位,那么第n位一定不是0,而且是后缀,所以从后往前枚举,而且没有is_limit限制!
题目代码

#include<bits/stdc++.h>

using namespace std;
const int N = 1e3 + 5;
int n, m, k,t;
int memo[N][105][2],POW[N];
inline int dfs(int i,int pre,bool flag,bool is_num){
    if(i == 0) return is_num && flag;
    if(is_num && ~memo[i][pre][flag]) return memo[i][pre][flag];
    int res = 0;
    if(!is_num) {
        res = dfs(i - 1,0,false,false) % m;
        for(int d = 1;d <= 9;++d){
            int x = d * POW[n - i] % k;
            res = (res + dfs(i - 1,x,!x,true)) % m;
        }
    }else{
        for(int d = (i == 1);d <= 9;++d){
            int x = (d * POW[n - i] % k + pre) % k;
            res = (res + dfs(i - 1,x,flag | !x,true)) % m;
        }
    }
    if(is_num) memo[i][pre][flag] = res;
    return res;
}
void solve() {
    cin >> n >> k >> m;
    POW[0] = 1;
    for(int i = 1;i <= n;++i) POW[i] = (POW[i - 1] * 10) % k;
    memset(memo,-1,sizeof(memo));
    cout << dfs(n,0,0,0) << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    t = 1;
    for (int _ = 0; _ < t; _++)
        solve();
    return 0;
}

posted @ 2024-04-05 09:55  gebeng  阅读(14)  评论(0)    收藏  举报