题解 P3799 【妖梦拼木棒】
无良宣传一下博客wwwwww
文章列表 - 核融合炉心 - 洛谷博客
知识点:组合数学/暴力枚举
-
题干:
有 \(n\) 根木棒,
Yoomu 现从中选 \(4\) 根,
想要组成一个 正三角形 ,
问有几种选法?
-
分析题意 :
由题干知 ,
欲由 \(4\) 根木棒 , 组成一个正三角形 ,
则必然有 两根 的 长度相同 ,
且另外两根的长度和 ,
等于前两根的 相等的木棒 的长度又发现 各木棍 的长度 \(a[i] \leqslant 5000\)
则可以直接用 两层循环 ,
暴力枚举 两种木棍 的长度
-
感性理解:
外层循环:
- 先要从 许多长度为 \(i\) 的木棒 中取出两个 ,
那么方案数为 :
从 \(num[j]\) 个数中取出 \(2\) 个数的组合 ( 即\(C(num[j],2)\) )
内层循环:
-
然后要从 剩下的木棒中 中取出两个
长度之和为 \(i\) 的木棒-
如果 \(j==i-j\) ,
那么要从长度为 \(j\) 的木棒中取出 \(2\) 个
那么方案数为:
从 \(num[j]\) 个数中取出 \(2\) 个数的组合
即\(C(num[j],2)\) -
如果\(j \ne i-j\)
那么要从长度为 \(j\) 的木棒中取出 \(1\) 个
再从长度为 \(i-j\) 的木棒中取出 \(1\) 个
同理 , 那么方案数为:
\(C(num[j],1) \times C(num[i-j],1)\)
-
根据乘法原理 ,
将所有方案数相乘 ,
即得 \(ans\) 的变化量 . - 先要从 许多长度为 \(i\) 的木棒 中取出两个 ,
-
算法分析:
用桶 \(num[i]\) ,
记录 各长度为\(i\) 的木棒出现的次数外层循环:
- 枚举两根相等的木棒的长度 \(i\) ,
当此长度的木棒数量 \(\geqslant 2\) 时 ,
进入内层循环
内层循环:
-
枚举一根用来 合成 的木棒长度 \(j\)
另一根木棒的长度即为 \(i-j\).
分两种情况进行讨论:-
1.如果 \((i==j \ ; \ num[j]\geqslant 1\ ;\ num[i-j]\geqslant 1)\) ,
\(ans+=C(num[i] , 2) \times C(num[j] , 1)\times C(num[i-j] , 1)\) -
2.如果 \((j==i-j\ ;\ num[j] \geqslant 2)\)
\(ans+= C(num[i],2) \times C(num[j],1)\)
-
- 枚举两根相等的木棒的长度 \(i\) ,
附 \(AC\) 代码 :
#include<cstdio>
#include<ctype.h>
#define int long long
#define max(a,b) a>b?a:b
//=======================================================
const int MARX = 1e6+10;
const int mod = 1e9+7;
int n,ans,maxa;
int a[MARX],num[MARX];
//=======================================================
inline int read()
{
int fl=1,w=0;char ch=getchar();
while(!isdigit(ch) && ch!='-') ch=getchar();
if(ch=='-') fl=-1;
while(isdigit(ch)){w=w*10+ch-'0',ch=getchar();}
return fl*w;
}
inline int C(int x,int k)//求得从n个数中取出k个数的组合
{
return k==1?x:x*(x-1)/2;
}
//=======================================================
signed main()
{
n=read();
for(int i=1;i<=n;i++)//读入,并放入桶中
a[i]=read(),
maxa=max(a[i],maxa),
num[a[i]]++;
for(int i=2;i<=maxa;i++)//枚举两根相等的边
if(num[i]>=2)
{
int times = C(num[i],2)%mod;//求出组合数
for(int j=1;j<=i/2;j++)//枚举被合成的边(枚举到i/2即可)
{
if(j!=i-j && num[j]>=1 && num[i-j]>=1)//当用来合成的木棒长度不等
ans=(ans + times*C(num[j],1)%mod*C(num[i-j],1)%mod)%mod;
if(j==i-j && num[j]>=2)//当用来合成的木棒长度相等
ans=(ans + times*C(num[j],2))%mod;
}
}
printf("%lld",ans%mod);
}
完成了这篇题解 , 车万众信仰\(++\)