DP小练(cf,atcoder)
Posted 佐鼬Jun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DP小练(cf,atcoder)相关的知识,希望对你有一定的参考价值。
F - Reordering
链接: link.
题意:
给定长度为 N N N的只包含小写字母的字符串 S S S,现在问有多少个字符串是由 S S S的子序列内的字符串排列形成的
思路:
d
p
(
i
,
j
)
dp(i,j)
dp(i,j)定义为用了前
i
i
i个字母(字母表中的字母,最多为
26
26
26个),组成长度为
j
j
j的字符串的数量
d
p
(
i
,
j
)
=
∑
k
=
0
m
i
n
(
j
,
c
n
t
[
i
]
)
d
p
(
i
−
1
,
j
−
k
)
×
C
j
k
dp(i,j)=\\sum_k=0^min(j,cnt[i]) dp(i-1,j-k)×C_j^k\\qquad
dp(i,j)=∑k=0min(j,cnt[i])dp(i−1,j−k)×Cjk
现在要往前
i
−
1
i-1
i−1个字母长度为
j
−
k
j-k
j−k的字符串中插入
k
k
k个第
i
i
i个字母了,由于之前的序列顺序通过排列组合已经有顺序了,所以只需要在长度
j
j
j的情况下,确定
k
k
k个插入的字母的相对位置,所以就是
C
j
k
C_j^k
Cjk
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 10;
#define int long long
const int mod = 998244353;
int dp[30][N];
string s;
int cnt[30];
int fact[N], infact[N];
int qmi(int a, int k)
int res = 1;
while (k)
if (k & 1) res = res * a % mod;
k >>= 1;
a = a * a % mod;
return res;
void init()
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i++)
fact[i] = fact[i - 1] * i % mod;
infact[i] = infact[i - 1] * qmi(i, mod - 2) % mod;
int C(int n, int m)
if (m > n) return 0;
return fact[n] * infact[m] % mod * infact[n - m] % mod;
signed main()
init();
cin >> s;
int len = s.length();
for (int i = 0; i < len; i++)
cnt[s[i] - 'a' + 1]++;
dp[0][0] = 1;
for (int i = 1; i <= 26; i++)
for (int j = 0; j <= len; j++)
for (int k = 0; k <= min(j, cnt[i]); k++)
dp[i][j] += dp[i - 1][j - k] * C(j, k) % mod;
dp[i][j] %= mod;
int res = 0;
for (int i = 1; i <= len; i++)
res += dp[26][i];
res %= mod;
cout << res << endl;
F - Variety of Digits
链接: link.
题意:
给定数字
M
M
M个数字
现在问
1
−
N
1-N
1−N中所有包含这
M
M
M个数字且没有前导
0
0
0的数字的和
思路:
定义
d
p
(
0
,
0
/
1
,
s
t
a
t
e
)
dp(0,0/1,state)
dp(0,0/1,state)为选择的数字在二进制下位
s
t
a
t
e
state
state情况下,现在选出来的数字是从高位到枚举的位置,是否小于对应位置的数字
N
N
N的个数
定义
d
p
(
1
,
0
/
1
,
s
t
a
t
e
)
dp(1,0/1,state)
dp(1,0/1,state)为选择的数字在二进制下位
s
t
a
t
e
state
state情况下,现在选出来的数字是从高位到枚举的位置,是否小于对应位置的数字
N
N
N的和
x
x
x为枚举的数字,循环是从高位往低位进行
n
d
p
ndp
ndp数组起到滚动数组的作用
这里选的数,是指
0
−
9
0-9
0−9这些数
那么此时
n
d
p
(
0
,
f
l
a
g
∣
∣
x
<
a
[
i
]
,
s
t
a
t
e
∣
1
<
<
x
)
+
=
d
p
(
0
,
f
l
a
g
,
s
t
a
t
e
)
ndp(0,flag||x<a[i],state|1<<x)+=dp(0,flag,state)
ndp(0,flag∣∣x<a[i],state∣1<<x)+=dp(0,flag,state)
n
d
p
(
1
,
f
l
a
g
∣
∣
x
<
a
[
i
]
,
s
t
a
t
e
∣
1
<
<
x
)
+
=
d
p
(
1
,
f
l
a
g
,
s
t
a
t
e
)
∗
10
+
d
p
(
0
,
f
l
a
g
,
s
t
a
t
e
)
∗
x
ndp(1,flag||x<a[i],state|1<<x)+=dp(1,flag,state)*10+dp(0,flag,state)*x
ndp(1,flag∣∣x<a[i],state∣1<<x)+=dp(1,flag,state)∗10+dp(0,flag,state)∗x
如果是枚举的最高位置,枚举时,跳过
0
0
0即可
由于是滚动数组,所以更新完后,再把
n
d
p
ndp
ndp赋值给
d
p
dp
dp即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
string s;
int m;
int dp[2][2][1 << 10];
signed main()
cin >> s;
int n = s.size();
vector<int> a(n);
for (int i = 0; i < n; i++)
a[i] = s[n - i - 1] - '0';
cin >> m;
int mask = 0;
for (int i = 0; i < m; i++)
int x;
cin >> x;
mask |= 1 << x;
for (int i = n - 1; i >= 0; i--)
int ndp[2][2][1 << 10];
memset(ndp, 0, sizeof(ndp));
for (int x = 1; x <= (i == n - 1 ? a[i] : 9); x++)
(ndp[0][i < n - 1 || x < a[i]][1 << x] += 1ll) %= mod;
(ndp[1][i < n - 1 || x < a[i]][1 << x] += x) %= mod;
for (int l = 0; l < 2; l++)
for (int s = 0; s < (1 << 10); s++)
for (int x = 0; x <= (l ? 9 : a[i]); x++)
(ndp[0][l || x < a[i]][s | 1 << x] += dp[0][l][s]) %= mod;
(ndp[1][l || x < a[i]][s | 1 << x] += dp[1][l][s] * 10 + dp[0][l][s] * x) %= mod;
memcpy(dp, ndp, sizeof(dp));
int res = 0;
for (int i = 0; i < 2; i++)
for (int j = 0; j < (1 << 10); j++)
if ((j & mask) == mask)
res += dp[1][i][j];
res %= mod;
cout << res << endl;
数位
d
p
dp
dp+记忆化的思路,基本差不多
d
f
s
(
p
o
s
,
s
t
a
t
e
,
l
e
a
d
,
l
i
m
i
t
)
dfs(pos,state,lead,limit)
dfs(pos,state,以上是关于DP小练(cf,atcoder)的主要内容,如果未能解决你的问题,请参考以下文章