P8329 ZJOI2022 树 题解 / 容斥
题目传送门:P8329 ZJOI2022 树。
设 \(F(S)\) 表示第一棵树的非叶子集合为 \(S\) 的方案数,\(G(T)\) 表示第二棵树的非叶子集合为 \(T\) 的方案数。
那么答案即为 \(ans=\sum\limits_{S \cap T = \varnothing,S \cup T=\{1,2,3,\cdots,n\}} F(s)G(T)\)。
考虑容斥设 \(F'(s)\) 表示第一颗树的非叶子集合包含于 \(S\) 的方案数,\(G'(T)\) 表示第二棵树的非叶子集合包含于集合 \(T\) 的方案数。
那么
\[\begin{aligned}
ans &= \sum\limits_{S \cap T = \varnothing,S \cup T=\{1,2,3,\cdots,n\}} F(s)G(T) \\
&= \sum\limits_{S \cap T = \varnothing,S \cup T=\{1,2,3,\cdots,n\}} \sum\limits_{S' \subset S} \sum\limits_{T'\subset T} F'(S') G'(T') (-1)^{|S|-|S'|+|T|-|T'|} \\
&= \sum\limits_{S' \cap T' = \varnothing} F'(S') G'(T') (-1)^{n-|S'|-|T'|} 2 ^{n-|S'|-|T'|} \\
&= \sum\limits_{S' \cap T' = \varnothing} F'(S') G'(T') (-2)^{n-|S'|-|T'|}
\end{aligned}
\]
相当于对于不在 \(S',T'\) 中的数带了 \(-2\) 的贡献。
考虑 dp,设 \(f_{i,j,k}\) 表示确定 \([1,i]\) 中 \(S',T'\) 的情况,且 \(|\{1,2,3,\cdots ,i\}\cap S'|=j,|\{i+1,i+2,\cdots n\}\cap T'|=k\) 的方案数。
转移的话考虑 \(i\) 属于哪里,并分配第一棵树 \(i\) 和第二棵树 \(i-1\) 父亲。
- 属于 \(S'\) 那么 \(f_{i,j,k}\leftarrow f_{i-1,j-1,k}(j-1)k\)。
- 属于 \(T'\) 那么 \(f_{i,j,k}\leftarrow f_{i-1,j,k+1}j(k+1)\)。
- 都不属于 \(f_{i,j,k}\leftarrow -2f_{i-1,j,k}jk\)
至于系数,父亲只能挂在非叶子节点上。
显然初始 \(f_{1,1,i}=1,1\le i <n\),最后答案即为 \(\sum f_{n-1,j,1} j\),要给第一棵树的 \(n\) 分配一个父亲。
#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
const int N=510;
int f[N][N][N],n,mod;
inline void add(int &x,int y){x=(x+y);if (x>=mod) x-=mod;}
main(){
n=read(),mod=read();
for (int i=1;i<n;i++) f[1][1][i]=1;
for (int i=2;i<=n;i++){
for (int j=1;j<=i;j++) for (int k=1;k<=n-i+1;k++){
add(f[i][j][k],f[i-1][j-1][k]*(j-1)*k%mod);
add(f[i][j][k],f[i-1][j][k+1]*j*(k+1)%mod);
add(f[i][j][k],mod-2*f[i-1][j][k]*j*k%mod);
}
int ans=0;
for (int j=1;j<i;j++) add(ans,f[i-1][j][1]*j%mod);
printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号