Description
Solution
考虑把问题转化为两问。
第一问为对于 k ∈ [ 1 , n ] k \in [1,n] k∈[1,n],选出 k k k个权值和 ≤ l i m \le lim ≤lim的水果的方案数。
第二问为把这 k k k个水果钦定为真香的生成树个数。把水果分为真香,香但不真香,不香的水果。其中第一类水果向一三类水果连边,第二类水果向第三类水果连边,第三类水果任意连边,矩阵树定理统计即可。注意求出的是至多 k k k个真香的方案数,还要容斥,容斥系数为组合数。
#include <bits/stdc++.h>
using namespace std;
typedef long long lint;
const int maxn = 45, mod = 1e9 + 7;
int n, m, lim, s[maxn];
vector<int> vec1[maxn], vec2[maxn];
int C[maxn][maxn];
int ans[maxn], g[maxn][maxn], Ans;
inline int gi()
{
char c = getchar(); bool f = 1;
while (c != '-' && (c < '0' || c > '9')) c = getchar();
if (c == '-') c = getchar(), f = 0;
int sum = 0;
while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return f ? sum : -sum;
}
inline int inc(int a, int b) {return a + b >= mod ? a + b - mod : a + b;}
inline int dec(int a, int b) {return a - b < 0 ? a - b + mod : a - b;}
inline int mul(int a, int b) {return (lint)a * b % mod;}
inline lint calc(int x, int y)
{
lint res = 0;
for (int i = 0, j = vec2[y].size() - 1, siz = vec1[x].size(); i < siz; ++i) {
while ((~j) && vec1[x][i] + vec2[y][j] > lim) --j;
res += j + 1;
}
return res;
}
#define add(i, j) --g[i][j], ++g[i][i]
int det(int a[maxn][maxn], int n)
{
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) a[i][j] = (a[i][j] + mod) % mod;
int ans = 1;
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j)
while (a[j][i]) {
int t = a[i][i] / a[j][i];
for (int k = i; k <= n; ++k) a[i][k] = dec(a[i][k], mul(t, a[j][k]));
swap(a[i], a[j]);
ans = mod - ans;
}
ans = mul(ans, a[i][i]);
if (!ans) return 0;
}
return ans;
}
int main()
{
n = gi(); lim = gi();
for (int i = 1; i <= n; ++i) {
s[i] = gi();
if (~s[i]) s[++m] = s[i];
}
for (int i = 0; i < (1 << (m / 2)); ++i) {
int x = 0, y = 0;
for (int j = 1; j <= m / 2; ++j)
if ((i >> (j - 1)) & 1) {
++x; y += s[j];
}
vec1[x].push_back(y);
}
for (int i = 0; i < (1 << (m - m / 2)); ++i) {
int x = 0, y = 0;
for (int j = m / 2 + 1; j <= m; ++j)
if ((i >> (j - m / 2 - 1)) & 1) {
++x; y += s[j];
}
vec2[x].push_back(y);
}
for (int i = 1; i <= m / 2; ++i) sort(vec1[i].begin(), vec1[i].end());
for (int i = 1; i <= m - m / 2; ++i) sort(vec2[i].begin(), vec2[i].end());
C[0][0] = 1;
for (int i = 1; i <= n; ++i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++j)
C[i][j] = inc(C[i - 1][j - 1], C[i - 1][j]);
}
for (int k = 0; k <= n; ++k) {
lint sum = 0;
for (int i = 0; i <= k; ++i) sum += calc(i, k - i);
memset(g, 0, sizeof(g));
for (int i = 1; i <= k; ++i) {
for (int j = 1; j <= k; ++j) add(i, j);
for (int j = m + 1; j <= n; ++j) add(i, j);
}
for (int i = k + 1; i <= m; ++i)
for (int j = m + 1; j <= n; ++j) add(i, j);
for (int i = m + 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) add(i, j);
ans[k] = det(g, n - 1);
for (int i = 0; i < k; ++i) ans[k] = dec(ans[k], mul(C[k][i], ans[i]));
Ans = inc(Ans, mul(ans[k], sum % mod));
}
printf("%d\n", Ans);
return 0;
}