#4707. 点分治

题目描述

题解

问题在于两棵已经确定点分树的形态的树,合并后能形成多少种形态的点分树

考虑如果分裂 $(u,v)$ 这条边,那就相当于两个点分树黑白染色,各自找相同颜色的祖先,然后形成新的两个点分树

考虑如果连接 $(u,v)$ 的话,那应该就是 $u->rt_u$ 和 $v->rt_v$ 这两条路径可以任意顺序合并起来,所以考虑树形dp,类似背包转移即可

效率: $O(n^2)$

代码

#include 
using namespace std;
const int N=5005,P=1e9+7;
int n,hd[N],V[N<<1],nx[N<<1],t,f[N][N],sz[N],c[N][N],g[N][N],F[N];
int X(int x){return x>=P?x-P:x;}
void add(int u,int v){
    nx[++t]=hd[u];V[hd[u]=t]=v;
}
void dfs(int u,int fr){
    f[u][sz[u]=1]=1;
    for (int v,i=hd[u];i;i=nx[i]){
        if ((v=V[i])==fr) continue;
        dfs(v,u);
        for (int j=1;j<=sz[u];j++)
            for (int i=0;i<=sz[v];i++)
                (F[i+j]+=1ll*f[u][j]*c[i+j-1][j-1]%P*g[v][i]%P)%=P;
        sz[u]+=sz[v];
        for (int i=1;i<=sz[u];i++)
            f[u][i]=F[i],F[i]=0;
    }
    for (int i=sz[u];~i;i--)
        g[u][i]=(g[u][i+1]+f[u][i])%P;
}
int main(){
    cin>>n;c[0][0]=1;
    for (int i=1;i<=n;i++){
        c[i][0]=1;
        for (int j=1;j<=i;j++)
            c[i][j]=X(c[i-1][j]+c[i-1][j-1]);
    }
    for (int i=1,x,y;i)
        scanf("%d%d",&x,&y),add(x,y),add(y,x);
    dfs(1,0);cout<1][0]<return 0;
}

 

你可能感兴趣的:(#4707. 点分治)