[ZJOI2022] 树
有两棵 \(n\) 个节点的有根树。
第一棵树 \(T_1\) 的根节点为 \(1\),\(i\) 的父亲节点 \(\in [1,i-1]\)。
第二棵树 \(T_2\) 的根节点为 \(n\),\(i\) 的父亲节点 \(\in [i+1,n]\)。
令 \(T_1\) 的非叶子节点构成的集合为 \(S_1\),\(T_2\) 的非叶子节点构成的集合为 \(S_2\)。
求不同的 \((T_1,T_2)\) 总数,使得 \(S_1 \cap S_2 = \varnothing\) 且 \(S_1 \cup S_2 = \{1,2,\dots,n\}\)。
你需要对 \([2,N]\) 中的每个 \(n\) 求出答案,对 \(M\) 取模。
\(2 \leq N \leq 500\),\(10 \leq M \leq 2^{30}\)。
考虑容斥。
记 \(f_1(S)\) 表示 \(T_1\) 的非叶子节点构成的集合 \(S_1 = S\) 时,对应的 \(T_1\) 方案数。
同理定义 \(f_2(S)\),此时答案写成:
考虑进行一点容斥。
记 \(f_1'(S)\) 表示 \(T_1\) 的非叶子节点构成的集合 \(S_1 \subseteq S\) 时,对应的 \(T_1\) 方案数。
同理,定义 \(f_2'(S)\),此时答案写成:
此时交换求和顺序,得:
即:
接下来考虑计算这个东西。
我们令 \(dp_{i,j,k}\) 表示确定了 \(\{1,2,\dots,i\}\) 中的点在 \(S_1\) 或 \(S_2\) 中的情况,且:
-
\(|\{1,2,\dots,i\} \cap S_1| = j\)
-
\(|\{i+1,i+2,\dots,n\} \cap S_2| = k\)
此时确定 \((1,i]\) 在第一棵树中的父亲,\([1,i)\) 在第二棵树中的父亲,总共的方案数。
显然,初始情况是 \(dp_{1,1,k} = 1\),接下来考虑 \(dp_{i-1,j,k}\) 的转移。
我们考虑 \(i\) 在树上的情况,显然有:
-
\(i\) 属于 \(S_1\),此时 \(dp_{i-1,j,k}\) 可以转移到 \(dp_{i,j+1,k}\),系数是 \(j \times k\)。
-
\(i\) 属于 \(S_2\),此时 \(dp_{i-1,j,k}\) 可以转移到 \(dp_{i,j,k-1}\),系数是 \(j \times k\)。
-
\(i\) 不属于 \(S_1\) 和 \(S_2\),此时 \(dp_{i-1,j,k}\) 可以转移到 \(dp_{i,j,k}\),系数是 \(-2 \times j \times k\)。
为什么是 \(-2\),因为这样才能保证在上面的式子中,\(-2\) 的次数是 \(n - |S_1| - |S_2|\)。
同时,\(j\) 的乘积表示 \(f_1'(S_1)\),\(k\) 的乘积表示 \(f_2'(S_2)\)。
复杂度 \(O(n^3)\)。
最后算答案,考虑 \(n=i\) 对应的答案,显然是 \(dp_{i-1,j,k}\) 只进行第二种转移的结果。
同时,因为是答案,所以 \(k=1\)。
这状态是咋想到的,位居了。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
using namespace std;
long long mod;
long long dp[510][510],new_dp[510][510];
int main(){
int n;
scanf("%d %lld",&n,&mod);
for(int i=1;i<n;i++){
dp[1][i]=1;
}
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++){
for(int k=1;k<=n-i;k++){
new_dp[j][k]=0;
}
}
long long ans=0;
for(int j=1;j<=i-1;j++){
for(int k=1;k<=n-i+1;k++){
//i in s1
if(j+1<=i){
new_dp[j+1][k]+=(long long)j*k*dp[j][k]%mod;
new_dp[j+1][k]%=mod;
}
if(k-1>=1){
new_dp[j][k-1]+=(long long)j*k*dp[j][k]%mod;
new_dp[j][k-1]%=mod;
}
new_dp[j][k]-=(long long)2*j*k*dp[j][k]%mod;
new_dp[j][k]=(new_dp[j][k]%mod+mod)%mod;
if(k==1){
ans+=(long long)j*k*dp[j][k]%mod;
ans%=mod;
}
}
}
for(int j=1;j<=i;j++){
for(int k=1;k<=n-i;k++){
dp[j][k]=new_dp[j][k];
}
}
printf("%lld\n",ans);
}
return 0;
}

浙公网安备 33010602011771号