枚举 组合数 P3799 妖梦拼木棒
P3799 妖梦拼木棒 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
思路:可以发现对于 n n n来说是1e5, O ( n 2 ) O(n^2) O(n2)算法不行,但是发现对于每一个 a i a_i ai来说,都是**小于5e3的正整数。**对此,我们可以考虑枚举1到5e3进行计算。
对于四个棍子要形成一个正三角形来说,一定是有两个棍子是相同的,剩下两个棍子累加等于之前的棍子,即对于i,j,k,z四个棍子来说,当且仅当 i = j i = j i=j 且 k + z = i k + z = i k+z=i时才能形成一个正三角形。
从1到5e3枚举两根棍子相同的情况,之后在找剩下两个棍子相加等于该棍子的情况,此时是 C c n t 2 C_{cnt}^2 Ccnt2。
对此,有两种情况:
- k = = z k == z k==z :这时计算是 C v 2 C_{v}^2 Cv2。
- k ! = z k != z k!=z:这时计算是 n u m k ∗ n u m z num_{k} * num_{z} numk∗numz
根据情况进行相乘并取模即可。计算时有除法操作,需要逆元。
代码如下:
const int mod = 1e9 + 7;
LL inv(LL a) {
if(a == 0 || a == 1) return a;
return (mod - mod/a) * inv(mod % a) % mod;
}
void solve() {
int n; cin>>n;
vector<int> a(5021);
for(int i = 0; i < n ;++i) {
int t; cin>>t;
a[t]++;
}
int ans = 0;
for(int i = 2; i <= 5000; ++i) {
if(a[i] < 2) continue;
int pre = (a[i] % mod* (a[i] - 1) % mod *inv(2)) % mod;;
for(int j = 1; j <= i / 2; ++j) {
if(j != i - j && a[j] > 0 && a[i - j] > 0) {
int t = pre * a[j] % mod * a[i - j] % mod;
ans = (ans + t) % mod;
}
if(j == i - j && a[j] > 1) {
int t = pre * (a[j] % mod* (a[j] - 1) % mod *inv(2) % mod);
ans = (ans + t) % mod;
}
}
}
cout<<ans<<endl;
}