洛谷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
尝试解决类似计数问题(如直角三角形计数)
原创内容 转载请注明出处
