洛谷 P3799. 小Y拼木棒 --- 排列与组合数的表示 暴力枚举

小 Y 拼木棒

题目背景

上道题中,小 Y 斩了一地的木棒,现在她想要将木棒拼起来。

题目描述

\(n\) 根木棒,现在从中选 \(4\) 根,想要组成一个正三角形,问有几种选法?

答案对 \(10^9+7\) 取模。

输入格式

第一行一个整数 \(n\)

第二行往下 \(n\) 行,每行 \(1\) 个整数,第 \(i\) 个整数 \(a_i\) 代表第 \(i\) 根木棒的长度。

输出格式

一行一个整数代表答案。

样例 #1

样例输入 #1

4 
1
1
2
2

样例输出 #1

1

提示

数据规模与约定

  • 对于 \(30\%\) 的数据,保证 \(n \le 5 \times 10^3\)
  • 对于 \(100\%\) 的数据,保证 \(1 \leq n \le 10^5\)\(1 \le a_i \le 5 \times 10^3\)

关于标题:因为一些不可抗力的原因,名称进行了更改。深表歉意。

题解


题解

我们要从n根木棒里选4根出来 拼成一个等边三角形
刚开始都看错题意了 选出来的四根是必须要求能拼成一个等边三角形的
关键点就是这个四根木棒拼成等边三角形
若能拼成 必然存在: 两根长度相等 且另外两根木棒长度之和等于前两根木棒的长度
太妙了我只能说 不论你是用两根木棒凑成三角形的一条边 还是一根木棒单独作为一条边 都是符合这个结论的
好了 接下来就枚举吧 分别枚举这两种木棒的长度 记录方案数
P3799-小-Y-拼木棒-洛谷-计算机科学教育新生态.png
来自 https://www.luogu.com.cn/article/5zvy12ri
我们来回顾一下组合数怎么求
1.png
所以
2.png
来自 https://www.luogu.com.cn/article/6p9hbrbq
现在我们把每层循环要干啥给搞清楚了 就可以开始实现代码了
注意计算过程中随时都要取mod 避免哪一环就给溢出了
也是debug了一会儿 像这种代码多的 就有很多细节 感觉也是debug量积累起来 才能一眼就看出的
以后代码提交ac不了 先留五分钟自己检查代码哪里出问题了 再去看答案 这也是对代码是否理解的一个考察

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10, mod = 1e9 + 7;

typedef long long LL;

int n, a[N], num[N]; //nums数组存的是长度为i的木棒有多少根 
int maxa; //我们需要记录最长的木棒长度大小 用来开枚举长度的边界 
LL times, ans; //times存枚举前两根木棒的组合情况 ans记录取出全部木棒的组合情况 

LL C(int x, int y) //求组合数的函数 
{
    if (y == 1) return x % mod; //取出一个数的情况 
    if (y == 2) return (x * (LL)(x - 1) / 2) % mod; //取出两个数的情况 
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        num[a[i]] ++ ; //a[i]长度的木棒个数++
        maxa = max(maxa, a[i]); 
    } 

    for (int i = 1; i <= maxa; i ++ ) //枚举两根长度相等的木棒 
    {
        if (num[i] >= 2) //确保当前枚举到的长度 存在着至少两根木棒
        {
            times = C(num[i], 2) % mod; //先求出从所有长度为i的木棒中取出2根的组合数
            for (int j = 1; j <= i / 2; j ++ ) //j只用枚举到2/i即可 后面的用i-j去表示 要不然会重复
            {
                if (j == i - j && num[j] >= 2) //如果剩下两根长度相等 那我们只需要从长度为j的木棒中取两根出来即可 
                {
                    ans += (times * C(num[j], 2)) % mod;
                    //cout << ans << endl;
                }
                if (j != i - j && num[i - j] >= 1 && num[j] >= 1) //如果剩下两根长度不一致 那我们就分别从长度为j和长度为i-j的木棒里取一根出来 再将其组合数相乘 这两根木棒的个数要求也只需要比一根多即可 
                {
                    ans += (times * C(num[i - j], 1) * C(num[j], 1)) % mod;
                    //cout << ans << endl;
                }
                ans %= mod; //ans+=之后也可能溢出 在这里也mod一下 
            }
        }
    }

    printf("%lld", ans);

    return 0;
}
posted @ 2024-04-16 16:15  MsEEi  阅读(264)  评论(0)    收藏  举报