题解 P3799 妖梦拼木棒
题意简述
有 \(n\) 根木棒,第 \(i\) 跟木棒的长度为 \(a_i\) ,现在从中选 \(4\) 根,想要组成一个正三角形,问有几种选法?
答案对 \(10^9+7\) 取模。
\(1 \leq n \leq 10^5 ,1 \leq a_i \leq 5\times 10^3\)。
Solution
选四根组成一个正三角形,也就是说必须选出两根长度相同的(设这个长度为 \(x\)),还要再选出两根长度和为 \(x\) 的木棒。
注意到 \(a_i \leq 5\times 10^3\) ,可以考虑先开个桶记录下每种木棒的长度有多少根,然后枚举这个 \(x\) , 再枚举拼成 \(x\) 的两根木棒的长度 \(i,j\) 即可通过。
那么接下来就是对于每个 \(x\) 分别计算了。记长度为 \(x\) 的木棒有 \(b_x\) 个,那么先从这 \(b_x\) 个木棒中选出 \(2\) 根,方案数是 \(\tbinom{b_x}{2}\) ,然后 \(i,j\) 的话要分两种情况:
- \(i \neq j\) ,那么相当于从长度为 \(i\) 的木棒里随便选一根,再从长度为 \(j\) 的木棒里随便选一跟,方案数为 \(b_i\times b_j\)。
- \(i=j\) ,那么相当于从长度为 \(i\) 的木棒里选出两根,方案数为 \(\tbinom{b_i}{2}\) 。
木棒最长为 \(x\) 的方案数是 \(\tbinom{b_x}{2}\) 乘枚举 \(i,j\) 时的方案和,原理是乘法原理。
总的方案数就是每种情况的和。注意乘法会爆 int
,要临时转化成 long long
。
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <iostream>
#include <queue>
typedef long long LL;
using namespace std;
inline int read() {
int num = 0 ,f = 1; char c = getchar();
while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
return num * f;
}
const int N = 5e3 + 5 ,mod = 1e9 + 7 ,inv2 = (mod + 1) / 2;
inline int C(int n) {
return (LL)n * (n - 1) % mod * inv2 % mod;
}
int cnt[N] ,ans ,n;
signed main() {
n = read();
for (int i = 1; i <= n; i++) {
int x = read();
cnt[x]++;
}
for (int i = 2; i <= 5000; i++)
if (cnt[i] >= 2) {
int res = 0;
for (int j = 1; j <= i / 2; j++)
if (i - j != j)
res = (res + (LL)cnt[j] * cnt[i - j] % mod) % mod;
else
res = (res + (LL)cnt[i / 2] * (cnt[i / 2] - 1) / 2 % mod) % mod;
ans = (ans + (LL)res * C(cnt[i]) % mod) % mod;
}
printf("%d\n" ,ans);
return 0;
}