P3799 小 Y 拼木棒
题目背景
上道题中,小 Y 斩了一地的木棒,现在她想要将木棒拼起来。
题目描述
有 n 根木棒,现在从中选 4 根,想要组成一个正三角形,问有几种选法?
答案对 109+7 取模。
输入格式
第一行一个整数 n。
第二行往下 n 行,每行 1 个整数,第 i 个整数 ai 代表第 i 根木棒的长度。
输出格式
一行一个整数代表答案。
输入输出样例
输入 #1复制
4 1 1 2 2
输出 #1复制
1
说明/提示
数据规模与约定
- 对于 30% 的数据,保证 n≤5×103。
- 对于 100% 的数据,保证 1≤n≤105,1≤ai≤5×103
卡了好长时间终于AC了呜呜呜。
题目分析
这道题不能使用dfs枚举每一种情况会超时,别问我怎么知道的。
改变思路,我们侧重于题目本身进行分析。要想利用4个木棒得到一个正三角形,首先得有两个相同的木棒,并且这个长度的木棒会比另外两个木棒的长度长。我们合理使用数组来存储每个长度木棒的数量,将数组a开到满足题目的最大值。
从大到小进行遍历,如果它的值a[i]大于等于2,则在1到i/2的范围内寻找满足题目情况的值。
这里使用到的还是重要的组合公式。两种物品分别有m和n个,每种里面都选择一种,则有m * n种组合。
这里给出一种关于没有顺序的cnm的计算代码(边乘边除法):
ll C(ll n, ll m) {
ll ans = 1;
for (ll i = 1; i <= m; i++) {
ans = ans * (n - m + i) / i;
}
return ans;
}
对于m == 2的情况我们直接可以返回n * (n - 1) / 2;
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7;
int n, a[5005] = {0};
int zuhe(int m){
if (m < 2) return 0;
return (ll)m * (m - 1) / 2 % mod;;
}
int main()
{
ll sum = 0;
cin >> n;
int tmp;
for (int i = 0; i < n; i++){
cin >> tmp;
a[tmp]++;
}
for(int i = 5001; i > 1; i--){
if(a[i] <= 1)continue;
else{// >= 2
int cm2 = zuhe(a[i]);
for(int j = 1; j <= i / 2; j++){//找匹配的数子
if(j != i - j){
if(a[j] > 0 && a[i - j] > 0){//可以相加的两个数都是大于0的
sum += a[j] * a[i - j] * cm2 % mod;
sum %= mod;
}
}else{// j == i - j
if(a[j] > 1)sum += zuhe(a[j]) * cm2 % mod;
sum %= mod;
}
}
}
sum %= mod;
}
cout << sum%mod;
}