题解 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\) 的变化量 .

  • 算法分析:

    用桶 \(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)\)


\(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);
}

完成了这篇题解 , 车万众信仰\(++\)


猜你喜欢

转载自www.cnblogs.com/luckyblock/p/11456240.html