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 {
#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; }
|