前置知识:排列组合,暴力枚举基础知识。
小 Y 拼木棒
题目背景
上道题中,小 Y 斩了一地的木棒,现在她想要将木棒拼起来。
题目描述
有 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
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, 1 ≤ a i ≤ 5 × 1 0 3 1 \le a_i \le 5 \times 10^3 1≤ai≤5×103。
关于标题:因为一些不可抗力的原因,名称进行了更改。深表歉意。
1.题意简述
找出四个数 a , b , c , d a,b,c,d a,b,c,d,求出有多少组这样的数使得 a = b = c + d ( c ≤ d ) a=b=c+d(c\leq d) a=b=c+d(c≤d)。
2.思路解析
大部分人第一时间想到的就是直接暴力枚举。使用枚举子集的方法,暴力枚举四条木棍的长度然后再判断。但是一看数据范围 n ≤ 1 0 5 n\leq 10^5 n≤105,那么用 O ( 2 n ) O(2^n) O(2n)的算法显然是不行的。要考虑优化。
因为组成三角形跟相同长度的木棍有关,首先使用类似于计数排序的方法,用pail[i]
储存长度为i
的木棍的根数。
我们可以考虑枚举
a
a
a,得到其中两根相同长度的木棍的长度(即a
和b
)。然后再枚举c
,通过c
来算出
d
d
d。这样只需要
O
(
n
2
)
O(n^2)
O(n2)的时间复杂度就搞定了。
枚举
c
c
c的时候,分两种情况讨论。第一种情况是c=d
。这个时候,就相当于从长度为
c
c
c的木棍里选取两根,与在长度为
a
a
a的木棍中选取的两根(
a
a
a和
b
b
b)组成一个三角形。这不就是计算组合数吗?即
C
p
a
i
l
[
a
]
2
∗
C
p
a
i
l
[
c
]
2
C_{pail[a]}^2*C_{pail[c]}^2
Cpail[a]2∗Cpail[c]2。
再来看看第二种情况,c!=d
。这个时候,就是从长度为c
的木棍中选取一根,再从长度为d
的木棍中选取一根,与在长度为
a
a
a的木棍中选取的两根组成一个三角形。即
C
p
a
i
l
[
a
]
2
∗
p
a
i
l
[
c
]
∗
p
a
i
l
[
d
]
C_{pail[a]}^2*pail[c]*pail[d]
Cpail[a]2∗pail[c]∗pail[d]。
现在再来看看如何计算组合数。可以发现,上面的组合数
C
n
m
C_n^m
Cnm只计算了
m
=
2
m=2
m=2的情况。我们可以据此化简,并将它封装成一个函数。
C
n
m
=
n
!
m
!
(
n
−
m
)
!
C_n^m=\frac{n!}{m!(n-m)!}
Cnm=m!(n−m)!n!
代入
m
=
2
m=2
m=2得:
n
!
2
!
(
n
−
2
)
!
\frac{n!}{2!(n-2)!}
2!(n−2)!n!
展开得:
1
×
2
×
3
×
⋅
⋅
⋅
⋅
⋅
⋅
×
(
n
−
1
)
×
n
1
×
2
×
1
×
2
×
3
×
⋅
⋅
⋅
⋅
⋅
⋅
×
(
n
−
3
)
×
(
n
−
2
)
\frac{1\times2\times3\times······\times(n-1)\times n}{1\times2\times1\times2\times3\times······\times(n-3)\times(n-2)}
1×2×1×2×3×⋅⋅⋅⋅⋅⋅×(n−3)×(n−2)1×2×3×⋅⋅⋅⋅⋅⋅×(n−1)×n
化简得:
n
(
n
−
1
)
2
=
n
(
n
−
1
)
/
2
\frac{n(n-1)}{2}=n(n-1)/2
2n(n−1)=n(n−1)/2
为了方便,我们在后续的代码中将其分装成一个宏函数,不要忘记宏函数的特性哦!
注意:
- 因为我们是使用
桶
来存储数据的,所以 a a a应当枚举到最大范围,即 5 × 1 0 3 = 5000 5×10^3=5000 5×103=5000;- 根据我们上面的定义 c ≤ d c\leq d c≤d,为了避免重复, c c c只需要枚举到
a/2
;- 计算的每一步都要确保长为
c
和长为d
的木棍存在,即pail[c]
和pail[d]
为真;- 记得运算中的每一步都要对
1e9+7
取模。
3.AC代码
#include<bits/stdc++.h>
using namespace std;
#define C(a) ((a)*((a)-1)/2)//计算组合数C(n,2),记得勤加括号
const int mod=1e9+7;
int main()
{
int n,pail[5010]={0},cnt=0;//用桶存下木棍的长度
cin>>n;
for(int i=1;i<=n;i++)
{
int tmp;
cin>>tmp;
pail[tmp]++;//放入桶中
}
for(int a=2;a<=5000;a++)//从2开始枚举a
{
for(int c=1;c<=a/2;c++)//c不可能超过a/2
{
int d=a-c;
//随时都要取模
if(d==c&&pail[a]>=2&&pail[c]>=2)//c和d相同
cnt+=((C(pail[a])%mod)*(C(pail[c])%mod))%mod;
else if(d!=c&&pail[a]>=2&&pail[c]&&pail[d])//c和d不相同
cnt+=((C(pail[a])%mod)*(pail[c]%mod)*(pail[d]%mod))%mod;
cnt%=mod;
}
}
cout<<cnt%mod;
return 0;
}
喜欢就订阅此专辑吧!
【蓝胖子编程教育简介】
蓝胖子编程教育,是一家面向青少年的编程教育平台。平台为全国青少年提供最专业的编程教育服务,包括提供最新最详细的编程相关资讯、最专业的竞赛指导、最合理的课程规划等。本平台利用趣味性和互动性强的教学方式,旨在激发孩子们对编程的兴趣,培养他们的逻辑思维能力和创造力,让孩子们在轻松愉快的氛围中掌握编程知识,为未来科技人才的培养奠定坚实基础。
欢迎扫码关注蓝胖子编程教育