Problem - 1310E - Codeforces

考虑倒推,每个数具体是啥是不知道的,但是我们可以知道数的种类。

本质上对于集合中的每一个元素,就代表一种不同的元素。

如果进行一次逆变换,元素种类就会变成原来集合 $\sum a_i$。

一开始是有限定的,集合大小小于等于 $n$。

先处理 $k = 1$ 的情况:划分数 $\tt Dp$。

$k = 2$ 的情况,就是考虑需要倒推两次,不妨考虑当前集合是 $c$,考虑 $b$ 中出现次数为 $c_i$ 的数是 $v_i$,可以得到 $\sum c_i v_i \le n$。

我们只需要考虑其存在性即可,我们不妨让 $c_i$ 递减,之后 $v_i = i$。这样可以取到最小值。

所以我们只需要满足 $\sum c_ii \le n$ 即可,考虑进行 $\tt Dp$ 因为钦定了 $c_i$ 的关系,我们需要二维的状态复杂度 $O(n ^ 3)$,但是事实上我们钦定 $c_i$ 所以只需要枚举差分值就行了。考虑再推式子。

我们让 $t_i = c_i - c_{i + 1}$。

$$
\begin{aligned}
\sum_i i \sum_{j = i} ^ m t_j &= \sum_{j = 1} ^ m t_j \sum_{i = 1} ^ j i \\
&= \sum_{j = 1} ^ m t_j \times \frac{(j + 1) j} {2}
\end{aligned}
$$

可以发现每个 $t_j$ 的贡献是 $\frac{(j + 1)j}{2}$。直接进行 $\tt Dp$ 完全背包。

考虑 $k =3$ 的情况:根据我们之前说过这个对于 $k$ 稍微大一点的话,函数的方案数会明显减少,我们不妨来证明一下。

考虑 $k = 3$ 的时候对于已经确定的权值 $m$ 怎么取到最小的 $n$,显然是填 $m$ 个 $1$ 最优。

那么对于上面一层就是 ${1,2,3,\cdots,m-1,m}$。权值是 $\frac{(m + 1)m}{2} \le n$。

显然 $\max_m = 64$,发现 $64$ 的拆分数是可以接受的,可以暴力枚举这个东西然后判断合法。

如果 $k \ge 4$ 会发现 $\max_m = 23$ 左右,这个可以直接进行剪枝保证复杂度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <bits/stdc++.h>
using namespace std;
namespace Legendgod {
namespace Read {
// #define Fread
#ifdef Fread
const int Siz = (1 << 21) + 5;
char *iS, *iT, buf[Siz];
#define gc() ( iS == iT ? (iT = (iS = buf) + fread(buf, 1, Siz, stdin), iS == iT ? EOF : *iS ++) : *iS ++ )
#define getchar gc
#endif
template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
x *= f;
}
template <typename T, typename...Args>
void r1(T &x, Args&...arg) {
r1(x), r1(arg...);
}
#undef getchar
}

using namespace Read;

const int maxn = 2020 + 5;
const int mod = 998244353;
int n, K;

template <typename T>
void Add(int& x,const T& c) { x = (x + c) % mod; }

namespace K1 {
int f[maxn], ans(0);
signed main() {
int i, j;
f[0] = 1;
for(i = 1; i <= n; ++ i) for(j = i; j <= n; ++ j)
Add(f[j], f[j - i]);
for(i = 1; i <= n; ++ i) Add(ans, f[i]);
printf("%d\n", ans);
return 0;
}
}

namespace K2 {
int f[maxn], ans(0);
signed main() {
int i, j;
f[0] = 1;
for(i = 1; i * (i + 1) / 2 <= n; ++ i) {
int c = (i + 1) * i / 2;
for(j = c; j <= n; ++ j)
Add(f[j], f[j - c]);
}
for(i = 1; i <= n; ++ i) Add(ans, f[i]);
printf("%d\n", ans);
return 0;
}
}

namespace K3 {
vector<int> vc;
int ans(0);

int check() {
vector<int> tmp = vc, tmp1; tmp1.clear();
int i, j;
for(i = K; i > 1; -- i) {
tmp1 = tmp, tmp.clear();
sort(tmp1.begin(), tmp1.end()), reverse(tmp1.begin(), tmp1.end());
int sum(0);
for(j = 0; j < tmp1.size(); ++ j) sum += tmp1[j] * (j + 1);
if(sum > n) return false;
if(i > 4 && sum > 23) return false;
for(j = 0; j < tmp1.size(); ++ j) {
while(tmp1[j] --) {
tmp.push_back(j + 1);
}
}
}
return true;
}

int dfs(int lim) {
if(!check()) return false;
Add(ans, 1);
for(int i = lim, fl; ; ++ i) {
vc.push_back(i), fl = dfs(i), vc.pop_back();
if(!fl) return true;
}
return true;
}

signed main() {
dfs(1), void();
printf("%d\n", ans == 0 ? mod - 1 : ans - 1);
return 0;
}

}

signed main() {
int i, j;
r1(n, K);
if(K == 1) return K1::main();
else if(K == 2) return K2::main();
else return K3::main();
return 0;
}

}


signed main() { return Legendgod::main(), 0; }//