洛谷P6686题解:组合数学在等腰三角形计数中的应用
一、问题背景
洛谷P6686题目要求计算使用给定长度木棍能组成的不同等腰三角形的数量。通过组合数学和三角形不等式,我们可以高效解决这一计数问题。
二、算法核心思想
三、完整代码实现(带详细注释)
#include <iostream> #include <vector> #include <algorithm> #include <unordered_map> using namespace std; const int MOD = 998244353; // 题目要求的模数 int main() { int n; cin >> n; vector<int> a(n); unordered_map<int, int> freq; // 记录每个长度出现的频率 for (int i = 0; i < n; ++i) { cin >> a[i]; freq[a[i]]++; // 统计每种长度的出现次数 } sort(a.begin(), a.end()); // 排序便于二分查找 long long ans = 0; // 最终结果 // 处理两条边相等的情况(等腰但不全等) for (auto it = freq.begin(); it != freq.end(); ++it) { int len = it->first; int cnt = it->second; if (cnt >= 2) { // 至少需要两根相同长度的木棍 int max_len = 2 * len - 1; // 三角形不等式:x < 2*len // 使用upper_bound找到第一个大于max_len的位置 auto upper = upper_bound(a.begin(), a.end(), max_len); // 计算满足条件的木棍总数 int total = upper - a.begin(); // 减去相同长度的木棍(避免三根相同的情况) total -= cnt; // 组合数计算:C(cnt,2) * total long long combinations = (long long)cnt * (cnt - 1) / 2 % MOD; ans = (ans + combinations * total % MOD) % MOD; } } // 处理三根边都相等的情况(等边三角形) for (auto it = freq.begin(); it != freq.end(); ++it) { int cnt = it->second; if (cnt >= 3) { // 组合数计算:C(cnt,3) long long combinations = (long long)cnt * (cnt - 1) * (cnt - 2) / 6 % MOD; ans = (ans + combinations) % MOD; } } cout << ans << endl; return 0; }
四、关键步骤解析
频率统计:使用unordered_map高效统计各长度出现次数
排序预处理:为后续二分查找创造条件
三角形不等式:确定第三边的有效范围(x < 2*len)
组合数计算:
C(cnt,2)计算选择两条相等边的方案数
C(cnt,3)计算等边三角形的方案数
五、学习建议
先理解三角形构成的基本条件
掌握组合数计算公式
练习使用STL的unordered_map和sort
尝试解决类似计数问题(如直角三角形计数)
原创内容 转载请注明出处