设 \(f(S)\) 表示钦定第一颗树叶子集合为 \(S\) 的方案数,则有 \(f(S) = \prod\limits_{i=2}^{n} (i - 1 - \sum\limits_{j=1}^{i-1}[j \in S])\)。同理,设 \(g(T)\) 表示钦定第二颗树中叶子集合为 \(T\) 的方案数。
枚举第一颗树的叶子集合恰好为 \(S\),第二颗树的叶子集合恰好为 \(T\),直接容斥则有:
\[\begin{align} ans &= \sum_{S, T} \sum_{x \subseteq T} \sum_{y \subseteq S} (-1)^{|x|+|y|} f(S \cup x) g(T \cup y) \\ &= \sum_{S \cup T = U} (-2) ^ {|S \cap T|} f(S) g(T) \end{align} \]\((2)\) 式已经可以直接 dp 计算了:从前往后依次给每个 \(i\) 分配:在 \(S\) 中 / 在 \(T\) 中 / 同时在 \(S\) 和 \(T\) 中即可。
设 \(f(i, s, t)\) 表示分配了 \([1,i]\),其中 \([1,i]\) 中有 \(s\) 个数在 \(S\) 中,\((i, n]\) 中还需要有 \(t\) 个数在 \(T\) 中的答案,转移容易,单次时间复杂度 \(\mathrm{O}(n^3)\)。
需要对 \([2, n]\) 分别做怎么办呢!这里有 \(n - i - t\) 的系数,不好一起做!但是把 \(s\) 和 \(t\) 改成不在 \(S\) 中和不在 \(T\) 中,系数就与 \(n\) 无关了,哈哈!
破防了。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
using ll = long long;
const int N = 510;
int MOD; ll f[N][N], g[N][N];
int main() {
cin.tie(0)->sync_with_stdio(0);
int N; cin >> N >> MOD;
for(int t = 0; t <= N; ++t) f[0][t] = 1;
for(int n = 1; n < N; ++n) {
for(int s = 0; s <= n; ++s)
for(int t = 0; t <= N - n + 1; ++t) if(f[s][t]) {
ll x = n == 1 ? 1 : s;
if(t) g[s][t - 1] = (g[s][t - 1] + f[s][t] * x % MOD * (t - 1)) % MOD;
g[s + 1][t] = (g[s + 1][t] + f[s][t] * x % MOD * t) % MOD;
g[s][t] = (g[s][t] + f[s][t] * x % MOD * t % MOD * (MOD - 2)) % MOD;
}
swap(f, g), memset(g, 0, sizeof(g));
ll ans = 0;
for(int s = 0; s <= n; ++s)
for(int t = 0; t <= 1; ++t) if(f[s][t]) {
if(t == 1) ans = (ans + f[s][t] * s) % MOD;
if(!t) ans = (ans + f[s][t] * s) % MOD;
if(!t) ans = (ans + f[s][t] * s % MOD * (MOD - 2)) % MOD;
}
printf("%lld\n", ans);
}
return 0;
}
标签:P8329,cup,int,sum,集合,ZJOI2022,include,ll
From: https://www.cnblogs.com/Aria-Math/p/18627830