数位DP
CF204A
题目链接
https://codeforces.com/problemset/problem/204/A
题目大意

模板讲解


数位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
题目大意

题目思路
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
题目大意

坑点
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
题目大意

思路
这题的思路很好想,主要是数学优化
代码版本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
题目大意

思路
求出[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
题目大意

题目思路
注意,有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;
}

浙公网安备 33010602011771号