「ZJOI2022」树
确实是很有意思的题,想清楚并不难
考虑 \(f[i][j][k]\) 表示前 \(i\) 个,\(T1\) 里有 \(j\) 个非叶子,\(T2\) 里有 \(k\) 个非叶子,但是这个时候会有一些非叶子实际上成为叶子
所以考虑容斥,枚举一个 \(T1\) 中叶子结点的子集 \(A\),设他的补集是 \(B\),那么答案就是
\[(\sum_{A\subseteq S}(-1)^{|S|-|A|}f(S))(\sum_{B\subseteq T}(-1)^{|T|-|B|}g(T))
\]
其中 \(f(S)\) 表示在 \(T1\) 里面 \(S\) 里面的点都是叶子,\(g(T)\) 同理
那么就可以 dp 了,有三种情况:
- 叶子-非叶子:\(f[i-1][j][k+1]jk\)
- 非叶子-叶子:\(f[i-1][j-1][k](j-1)k\)
- 叶子-叶子:\(-2f[i-1][j][k]jk\),注意这个点既可以给 \(A\) 也可以给 \(B\),所以 -2
复杂度 \(O(n^3)\)
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
const int N=505;
typedef long long ll;
typedef double db;
# define chkmax(a,b) a=max(a,b)
# define chkmin(a,b) a=min(a,b)
# define PII pair<int,int>
# define mkp make_pair
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,mod;
ll f[N][N][N];
int main()
{
# ifndef ONLINE_JUDGE
freopen("testdata.in","r",stdin);
//freopen("test1.out","w",stdout);
# endif
read(n),read(mod);
Rep(i,1,n)f[1][1][i]=i;
Rep(i,2,n){
Rep(j,1,i)
Rep(k,1,n-i){
ll val=0;
val+=f[i-1][j][k+1]*j*k;
val+=f[i-1][j-1][k]*(j-1)*k;
val-=2*f[i-1][j][k]*j*k;
f[i][j][k]=val%mod;
}
ll ans=0;
Rep(j,1,i)ans+=f[i-1][j][1]*j;
printf("%lld\n",(ans%mod+mod)%mod);
}
return 0;
}