2025 多边形 · 方案二 排序加计数 DP
枚举最长边,总和封顶后做计数背包
正文
// CSP-J 2025 复赛 T4 · 多边形(方案二 · 满分)
// 原题:https://oj.yecheng.tv/p/CSP2025JD
//
// 方案二 · 排序 + 按「总和」计数 DP
// 关键一:先把木棍按长度排序。规定每个合法选法只在「它里面下标最大的那根」处被统计一次,
// 于是枚举这根最靠后的木棍 a[i],其余的只能从它前面那 i-1 根里挑。
// 这样一来 a[i] 天然就是选法里的最长边,条件变成「前面挑出的总和 > a[i]」且至少挑 2 根。
// 关键二:a[i] 最大只有 5000,所以总和只要超过 5000 就一定能满足条件,
// 再大的和没必要区分 —— 把总和「封顶」在 5001 就行,DP 数组一下就小了。
// 于是维护两个数组:
// dp1[s] = 只从前面挑、恰好挑 1 根、总和为 s 的选法数;
// dp2[s] = 只从前面挑、挑了 2 根或更多、总和为 s 的选法数。
// 每处理一根新木棍 x:先查 dp2 里总和 > x 的部分(后缀和)加进答案,
// 再把 x 并进 DP(分「不选 x」和「选 x」两部分,用一份拷贝避免重复统计)。
// 复杂度 O(n * 5001),n = 5000 时约 2.5e7,稳过。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 998244353;
const int CAP = 5001; // 总和封顶:a[i] <= 5000,超过 5000 就一定满足条件
long long dp1[CAP + 1], dp2[CAP + 1];
long long ndp1[CAP + 1], ndp2[CAP + 1];
long long suf[CAP + 2];
int main() {
freopen("polygon.in", "r", stdin);
freopen("polygon.out", "w", stdout);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
sort(a.begin(), a.end());
memset(dp1, 0, sizeof(dp1));
memset(dp2, 0, sizeof(dp2));
long long ans = 0;
for (int i = 0; i < n; i++) {
int x = a[i];
// 后缀和:suf[s] = dp2[s] + dp2[s+1] + ... + dp2[CAP]
suf[CAP + 1] = 0;
for (int s = CAP; s >= 0; s--) suf[s] = (suf[s + 1] + dp2[s]) % MOD;
ans = (ans + suf[x + 1]) % MOD; // 挑至少 2 根且总和 > x
// 把 x 并进 DP:先拷一份表示「不选 x」
for (int s = 0; s <= CAP; s++) {
ndp1[s] = dp1[s];
ndp2[s] = dp2[s];
}
int ns1 = x > CAP ? CAP : x;
ndp1[ns1] = (ndp1[ns1] + 1) % MOD; // 只选 x 这一根
for (int s = 0; s <= CAP; s++) {
int ns = s + x;
if (ns > CAP) ns = CAP;
ndp2[ns] = (ndp2[ns] + dp1[s] + dp2[s]) % MOD; // 原来挑 1 根或更多,再选上 x
}
for (int s = 0; s <= CAP; s++) {
dp1[s] = ndp1[s];
dp2[s] = ndp2[s];
}
}
cout << ans << "\n";
return 0;
}
CSP-J 标程 · 复赛真题的其它内容
- 2019 数字游戏 · 方案一 逐字符统计
- 2019 数字游戏 · 方案二 二进制位运算
- 2020 优秀的拆分 · 方案一 贪心从大到小减
- 2020 优秀的拆分 · 方案二 直接看二进制位
- 2021 分糖果 · 方案一 枚举每个 k
- 2021 分糖果 · 方案二 看余数在哪一段
- 2022 乘方 · 方案一 边乘边判断
- 2022 乘方 · 方案二 快速幂加封顶
- 2023 小苹果 · 方案一 照规则真模拟
- 2023 小苹果 · 方案二 只盯住两个数字
- 2024 扑克牌 · 方案一 用集合去重
- 2024 扑克牌 · 方案二 二维布尔表
- 2025 拼数 · 方案一 收集后降序排序
- 2025 拼数 · 方案二 桶计数
- 2019 公交换乘 · 方案一 暴力匹配
- 2019 公交换乘 · 方案二 时间窗口优化
- 2020 直播获奖 · 方案一 每轮排序
- 2020 直播获奖 · 方案二 桶计数
- 2021 插入排序 · 方案一 每次真排一遍
- 2021 插入排序 · 方案二 增量维护有序表
- 2022 解密 · 方案一 二分求整数平方根
- 2022 解密 · 方案二 先估后校开方
- 2023 公路 · 方案一 朴素贪心
- 2023 公路 · 方案二 单调栈预处理
- 2024 地图探险 · 方案一 四方向分支写
- 2024 地图探险 · 方案二 方向数组
- 2025 座位 · 方案一 把座位表填出来
- 2025 座位 · 方案二 直接算排名
- 2019 纪念品 · 方案一 逐天完全背包
- 2019 纪念品 · 方案二 砍掉不赚钱物品
- 2020 表达式 · 方案一 每次重算后缀式
- 2020 表达式 · 方案二 建树加关键性传播
- 2021 网络连接 · 方案一 手写解析
- 2021 网络连接 · 方案二 读入后回拼校验
- 2022 逻辑表达式 · 方案一 递归分治
- 2022 逻辑表达式 · 方案二 递归下降
- 2023 一元二次方程 · 方案一 照公式逐步写
- 2023 一元二次方程 · 方案二 拆成小函数
- 2024 小木棍 · 方案一 先定位数再贪心
- 2024 小木棍 · 方案二 预处理最小数字表
- 2025 异或和 · 方案一 贪心能接就接
- 2025 异或和 · 方案二 动态规划加值域数组
- 2019 加工零件 · 方案一 递归加记忆化
- 2019 加工零件 · 方案二 奇偶最短路
- 2020 方格取数 · 方案一 深搜回溯
- 2020 方格取数 · 方案二 按列动态规划
- 2021 小熊的果篮 · 方案一 每轮扫一遍
- 2021 小熊的果篮 · 方案二 链表加有序集合
- 2022 上升点列 · 方案一 先只考虑不加点
- 2022 上升点列 · 方案二 二维动态规划
- 2023 旅游巴士 · 方案一 分层图加优先队列
- 2023 旅游巴士 · 方案二 状态压成一维
- 2024 接龙 · 方案一 按定义广搜
- 2024 接龙 · 方案二 滑动窗口逐轮推进
- 2025 多边形 · 方案一 枚举所有子集