2 条题解
-
-1
[CSP-J 2025] 多边形 题解
题目分析
本题要求从 根小木棍(长度分别为 )中选择至少 3 根,使得拼成多边形。 多边形拼合条件:
求选出小木棍的合法方案数对 取模的结果。
核心转化
-
确定最长边: 将所有木棍升序排序:。 我们枚举选出的木棍集合中下标最大(最长)的木棍为第 根(长度为 )。 则集合中其余被选中的木棍必定来自下标 。
-
化简多边形条件: 设其余选中的木棍长度之和为 。 根据条件:
注意:
- 因为每个木棍长度 ,若 ,由于集合内其他任何单根木棍的长度都 ,所以必然至少选择了 2 根其他木棍;
- 加上第 根木棍本身,选出的木棍总数至少为 根,自动满足 的条件!
-
正难则反(背包 DP): 对于第 根木棍,在前面的 根木棍中,共有 种不同的选取子集。 其中不合法的方案数,就是其余木棍长度和 的方案数。 因此:
$$\text{以 } a_i \text{ 为最大边的合法方案数} = 2^{i-1} - \sum_{S=0}^{a_i} (\text{前 } i-1 \text{ 根木棍中和为 } S \text{ 的方案数})$$
动态规划设计
- 题目保证 。
- 定义状态 表示当前已经处理过的元素中,子集和为 的方案数。
- 初始状态:,其余 。
- 遍历 从 到 :
- 计算 $\text{invalid} = \sum_{s=0}^{a_i} dp[s] \pmod{998244353}$;
- $\text{ans} = (\text{ans} + 2^{i-1} - \text{invalid}) \pmod{998244353}$;
- 将当前木棍 加入背包: 倒序更新 从 到 :
- 遍历结束后, 即为最终答案。
复杂度分析
- 时间复杂度:。 对于 , 次运算,在 C++ 中耗时在 0.1 秒左右,轻松通过。
- 空间复杂度:,仅占用微量内存。
参考代码 (C++)
#include <iostream> #include <vector> #include <algorithm> using namespace std; const int MOD = 998244353; const int MAX_A = 5000; int dp[MAX_A + 1]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; if (!(cin >> n)) return 0; vector<int> a(n); for (int i = 0; i < n; ++i) { cin >> a[i]; } sort(a.begin(), a.end()); dp[0] = 1; long long ans = 0; long long pow2 = 1; for (int i = 0; i < n; ++i) { int val = a[i]; long long invalid = 0; for (int s = 0; s <= val && s <= MAX_A; ++s) { invalid = (invalid + dp[s]) % MOD; } ans = (ans + pow2 - invalid + MOD) % MOD; for (int s = MAX_A; s >= val; --s) { dp[s] = (dp[s] + dp[s - val]) % MOD; } pow2 = (pow2 * 2) % MOD; } cout << ans << "\n"; return 0; }参考代码 (Python 3)
import sys def main(): input_data = sys.stdin.read().split() if not input_data: return n = int(input_data[0]) a = [int(x) for x in input_data[1:n+1]] MOD = 998244353 a.sort() max_a = 5000 dp = [0] * (max_a + 1) dp[0] = 1 ans = 0 pow2 = 1 for i in range(n): val = a[i] invalid = sum(dp[:min(val + 1, max_a + 1)]) % MOD ans = (ans + pow2 - invalid) % MOD for s in range(max_a, val - 1, -1): dp[s] = (dp[s] + dp[s - val]) % MOD pow2 = (pow2 * 2) % MOD print(ans) if __name__ == "__main__": main() -
-
-1
题解:基础除法
题意重述
题目给定两个整数 和 (),要求计算并输出 除以 向下取整的结果。
思路
本题主要考察基础的数学运算与取整逻辑处理。 在 C++ 语言中,整数除法运算符
/的默认规则是向零取整。当结果为正数时,向零取整与向下取整是一致的;但当真实结果为负小数时(例如 ),向零取整的结果是 ,而题目要求的向下取整应该为 。因此,我们需要分情况来处理:
- 首先计算出普通的整数除法结果
res = a / b和余数mod = a % b。 - 若
mod != 0且 与 符号相反(即异号),说明真实的商为负小数,此时应当将res减 。 - 否则,
res已经符合向下取整的要求,直接输出即可。
代码
#include <iostream> using namespace std; int main() { long long a, b; cin >> a >> b; long long res = a / b; long long mod = a % b; // 异号且有余数时需再减一 if (mod != 0 && ((a < 0) ^ (b < 0))) { res = res - 1; } cout << res << '\n'; return 0; } - 首先计算出普通的整数除法结果
- 1
信息
- ID
- 7
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者