AtCoder [ARC101E] Ribbons on Tree

$O(n^3)$ 的暴力 $\text{DP}$ 比较好想,但是貌似无法优化了

考虑既然有如此强的限制,要求每一条边都被覆盖过,可以尝试使用容斥

设 $E$ 为没有覆盖到的边集, $F(E)$ 为 $E$ 中的边没有覆盖到(其他不管)的整棵树的方案数

于是用 $E$ 相当于将这棵树分割成了许多联通块,每个联通块里面只能选自己联通块的,那么每个联通块的方案数为

$h(size)=(size-1) \times (size-3) \times ... \times 3 \times 1$ (前提是 $2 \mid size$ ,不然方案数显然为 $0$ )

于是我们可以换种方式 $\text{DP}$ 

设 $f_{i,j}$ 为以 $i$ 为根的子树内 $i$ 所在的联通块大小为 $j$ 的方案数(不删掉 $i$ 所在的联通块)

所以转移就按容斥一样转移,如果删掉一棵子树,那就要减去删掉的方案数(差不多是这个意思),否则直接加上方案数

最后答案显然就是

$\sum\limits_{i=2}^n f_{1,i} \times h(i)$

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 5555
#define mod 1000000007

template<class T>

inline T read(){
    T r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

template<class T>

inline T min(T a,T b){
    return a<b?a:b;
}

struct E{
    int v,nxt;
    E() {}
    E(int v,int nxt):v(v),nxt(nxt) {}
}e[maxn<<1];

int n,s_e,head[maxn],size[maxn];

long long ans,h[maxn],g[maxn],f[maxn][maxn];

inline void a_e(int u,int v){
    e[++s_e]=E(v,head[u]);
    head[u]=s_e;
}

void dfs(int u,int fa){
    f[u][1]=1;
    size[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        for(int j=1;j<=size[u]+size[v];j++)g[j]=0;
        for(int j=1;j<=size[u];j++)
            for(int k=1;k<=size[v];k++){
                (g[j]+=(mod-f[u][j]*f[v][k]%mod*h[k])%mod)%=mod;//删掉v这棵子树里的,所以要减去
                (g[j+k]+=f[u][j]*f[v][k]%mod)%=mod;//不然直接累加
            }
        for(int j=1;j<=size[u]+size[v];j++)f[u][j]=g[j];
        size[u]+=size[v];
    }
}

int main(){
    n=read<int>();
    for(int i=1;i<n;i++){
        int u=read<int>(),v=read<int>();
        a_e(u,v),a_e(v,u);
    }
    h[0]=1;
    for(int i=2;i<=n;i+=2)
        h[i]=h[i-2]*(i-1)%mod;
    dfs(1,0);
    ans=0;
    for(int i=2;i<=n;i+=2)
        (ans+=f[1][i]*h[i]%mod)%=mod;
    printf("%lld\n",ans);
    return 0;
}
posted @ 2020-11-23 16:31  一叶知秋‘  阅读(141)  评论(0编辑  收藏  举报