「ZJOI2022」树

link

确实是很有意思的题,想清楚并不难
考虑 \(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;
}
posted @ 2022-05-07 20:59  YuukiYumesaki  阅读(42)  评论(0编辑  收藏  举报