妖梦拼木棒
题目背景
上道题中,妖梦斩了一地的木棒,现在她想要将木棒拼起来。
题目描述
有 n n n 根木棒,现在从中选 4 4 4 根,想要组成一个正三角形,问有几种选法?
答案对 1 0 9 + 7 10^9+7 109+7 取模。
输入格式
第一行一个整数 n n n。
第二行往下 n n n 行,每行 1 1 1 个整数,第 i i i 个整数 a i a_i ai 代表第 i i i 根木棒的长度。
输出格式
一行一个整数代表答案。
样例 #1
样例输入 #1
4
1
1
2
2
样例输出 #1
1
提示
数据规模与约定
- 对于 30 % 30\% 30% 的数据,保证 n ≤ 5 × 1 0 3 n \le 5 \times 10^3 n≤5×103。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 5 1 \leq n \le 10^5 1≤n≤105, 0 ≤ a i ≤ 5 × 1 0 3 0 \le a_i \le 5 \times 10^3 0≤ai≤5×103。
题解:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#define ll long long
#define int ll
using namespace std;
int cnt[5001] = { 0 }, n;
const int mod = 1e9 + 7;
ll C(ll m, ll n) {
return m == 1ll ? n : n * (n - 1ll) / 2ll % mod;
}
signed main()
{
//freopen("in.txt", "r", stdin);
ll ans = 0, mmax = -1;
cin >> n;
for (int i = 0; i < n; i++) {
int t; cin >> t;
mmax = t > mmax ? t : mmax;
cnt[t]++;
}
for (int i = 2; i <= mmax; i++) {
if (cnt[i] >= 2) {
ll temp = C(2, cnt[i]);
for (int j = 1; j <= i / 2; j++) {
int t1 = j, t2 = i - j;
if (t1 == t2 && cnt[t1] >= 2) {
ans += temp * C(2, cnt[t1]) % mod;
}
else if (t1 != t2 && cnt[t1] > 0 && cnt[t2] > 0) {
ans += temp * C(1, cnt[t1]) * C(1, cnt[t2]) % mod;
}
ans %= mod;
}
}
}
cout << ans;
return 0;
}
思路:
- 审题:
- 题目要求选四根木棒拼成一个正三角形,正三角形意味着三角形三边相等,但偏偏题目规定要用四根木棒来拼,所以只可能出现一种情况:有两根木棒是等长的,另外两根木棒只能拼起来和它们等长。
- 构思代码框架:
- 题目给出的数据范围规定木棒的长度在 [1,5000] 之间,所以我们可以使用5000个桶来统计每个木棒长度出现的次数。
- 然后在最外层循环枚举两根等长木棒,将这个长度记为i,如果遇到不足两根的长度就进入下一次循环。每一次从n根木棒里取出两根木棒,根据组合数计算公式可以得到方法数为C(2, num),由于没有比1更小的长度,并且也不能出现三根木棒等长的情况,所以外层循环从2开始;
- 然后在内层循环枚举两根拼接木棒的长度,因为这两根木棒长度之和要等于i,所以它们的长度范围便是[1, i ),将它们的长度分别记为t1 = j, t2 = i - j,则j的枚举范围便是[1, i / 2]。
- 而这两根木棒的长度也可能有两种情况:
- t1 == t2
- t1 < t2
- 用文字来解释就是,两根木棒可能长度相等,也可能长度不等。
- 针对两种不同的情况,求方法数的方法也有所不同。长度相等时,便相当于要从长度为t1的一堆木棒中选两根,首先,判断木棒的个数是否大于等于2,然后使用组合数公式C(2,n)计算方法数;长度不相等时,也就是分别从长度为t1的一堆木棒和长度为t2的一堆木棒里分别选一根拼起来,所以方法数就是C(1, num1) * C(1, num2)。
- 将按照以上不同情况得出的结果与外层循环得到的方法数相乘得到一种情况的方法数,然后更新答案ans的值,并不断取模。
- 而这两根木棒的长度也可能有两种情况: