11.17比赛题解
A. 圣诞树
和度数相关的数树题可以考虑 prufer 序列。
prufer 序列的性质是,一个数在其中出现次数加一是其度数。
如果称一个点上孔的数量为 \(a_i\),称一种情况中 i 点的度数是 \(d_i\),那么我们的答案就是:
\[\displaystyle\sum_{\sum {d_i - 1 = n - 2}} \binom{n - 2}{d_1 - 1} \binom{n - 2 - (d_1 - 1)}{d_2 - 1} ..... \binom{b_n - 1}{b_n - 1} \prod_{i = 1}^n a_i^{\underline{d_i}}
\]
可以发现,前面的组合数相乘的部分就是:
\[(n - 2)!\sum_{\sum {d_i - 1 = n - 2}} \prod\frac{1}{(d_i - 1)!}
\]
考虑把后面两项合并:
\[(n - 2)!\sum_{\sum {d_i - 1 = n - 2}} \prod\frac{a_i!}{(d_i - 1)!(a_i - d_i)!}
\]
后面的东西很像组合数:
\[(n - 2)!\sum_{\sum {d_i - 1 = n - 2}} \prod a_i\frac{(a_i - 1)!}{(d_i - 1)!(a_i - d_i)!}\\
\Leftrightarrow (n - 2)!\prod a_i \sum_{\sum {d_i - 1 = n - 2}} \prod \binom{a_i - 1}{d_i - 1}
\]
考虑组合意义,后面的东西就是:
\[(n - 2)!\prod a_i \sum_{\sum {d_i - 1 = n - 2}} \binom{\sum( a_i - 1)}{\sum (d_i - 1)}\\
\Leftrightarrow
(n - 2)!\prod a_i \binom{\sum( a_i - 1)}{n - 2}
\]
直接计算即可。
考场上模数不一定是质数,把 \((n - 2)!\) 消掉就行了。
代码
#include <bits/stdc++.h>
using namespace std;
namespace myb {
using ll = long long;
const int N = 2e5 + 10;
const int Mod = 998244353;
int a[N];
void main() {
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
ll ans = 1;
for (int i = 1;i <= n ;i++) ans = (__int128)ans * a[i] % Mod;
ll S = 0;
for (int i = 1;i <= n;i++) S += a[i] - 1;
// cout << S << " " << ans << "\n";
for (ll i = S;i > S - n + 2;i--) ans = (__int128)ans * i % Mod;
cout << ans;
// \prod a_i S!/(S - n - 2)!
}
}
int main() {
myb::main();
return 0;
}
B. 序列
我们从值域的角度去想,如果我当前将所有大于 i 的数已经加进去了,现在我们要将所有的 i 插入这个序列中。
考虑如果现在我有 j 个连续段,那么如果我插入一个 i 不使段数增加,那么必须插在段尾,否则 i 会使段数增加。
我可以枚举不使段数增加的 i 有 x 个,剩下 \(a_i - x\) 个要插进原本连续段的两个数中间去,使连续段增加 \(a_i - x\) 个。
因为可以有两个 i 插入一个缝中,所以我们考虑插板法。
一共 \(a_i - x\) 个数,\(\sum_{t = i}^n a_t - j + x\) 个板。
则方程为
f[i][j + a[i] - x] += f[i][j] * C(j, x) * C(sum[i + 1] - j + x, sum[i + 1] + a[i] - j)
代码
#include <bits/stdc++.h>
using namespace std;
namespace myb {
#define int long long
using ll = long long;
const int N = 55;
const int Mod = 1e9 + 7;
int a[N];
ll f[N][N * N], C[3005][3005], sum[N];
void main() {
// cout << 114514;
int n, k;
cin >> n >> k;
for (int i = 1;i <= n;i++) cin >> a[i];
C[0][0] = 1;
for (int i = 1;i <= 3000;i++) {
C[i][0] = 1;
for (int j = 1;j <= min(3000ll, i);j++) {
C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
C[i][j] %= Mod;
}
}
for (int i = n;i >= 1;i--) sum[i] = sum[i + 1] + a[i];
// cout << C[4][2] << '\n';
f[n][a[n]] = 1;
for (int i = n - 1;i >= 1;i--) {
for (int j = 1;j <= k;j++) {
for (int x = 0;x <= min(j, a[i]);x++) {
if (j + a[i] - x > k) continue;
if (sum[i + 1] + a[i] - j < 0) continue;
if (sum[i + 1] + x - j < 0) continue;
f[i][j + a[i] - x] += f[i + 1][j] * C[j][x] % Mod * C[sum[i + 1] + a[i] - j][sum[i + 1] - j + x] % Mod;
f[i][j + a[i] - x] %= Mod;
}
}
}
cout << f[1][k];
}
}
signed main() {
freopen("seq.in", "r", stdin);
freopen("seq.out", "w", stdout);
myb::main();
return 0;
}

浙公网安备 33010602011771号