UVALive 5002/ lightoj 1382 The Queue(树形DP)

题意:有n个人,除BOSS外,每个人都有一个上司,现在要排队,每个人都不能排在自己的上司前面,问有几种排法。


思路参考大神的吧:


首先求出以每个结点为根的子树大小,记为size[u],这个DFS一遍就可以求出来;

  • 接下来,dp[u]表示给以u为根的子树size[u]个编号有几种编号方案 
  • 然后考虑转移方程:
    • 比如一个结点u有3个儿子v1,v2,v3,那么u子树有size[u]个编号,根就属于u,剩下size[u]-1分配给u的三个子树,分配方式就有:

C(size[u]-1,size[v1])*C(size[u]-1-size[v1],size[v2])*C(size[u]-1-size[v1]-size[v2],size[v3]) 

    • 而v1、v2和v3子树对它们被分配的编号又分别有dp[v1]、dp[v2]和dp[v3]种编号方案,因此u子树的size[u]个编号总共的编号方式即dp[u]是:

dp[u] = dp[v1]*dp[v2]*dp[v3]*C(size[u]-1,size[v1])*C(size[u]-1-size[v1],size[v2])*C(size[u]-1-size[v1]-size[v2],size[v3])

于是就是这样用DP求解的。另外C(n,m)可以利用组合数的递推式C(n,m)=C(n-1,m)+C(n-1,m-1)预处理出来。


#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn = 1e3+5;
ll dp[maxn], cnt[maxn], C[maxn][maxn];
bool book[maxn];
vector g[maxn];

void init()
{
    for(int i = 0; i < maxn; i++)
    {
        C[i][0] = C[i][i] = 1;
        for(int j = 1; j < i; j++)
            C[i][j] = (C[i-1][j-1]+C[i-1][j])%mod;
    }
}

void dfs(int u)
{
    dp[u] = 1;
    for(int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i];
        dfs(v);
        cnt[u] += cnt[v];
        dp[u] = (dp[u]*C[cnt[u]][cnt[v]])%mod*dp[v]%mod;
    }
    cnt[u]++;   //加上根这个点
}

int main(void)
{
    init();
    int t, n, ca = 1;
    cin >> t;
    while(t--)
    {
        for(int i = 0; i < maxn; i++)
            g[i].clear();
        memset(book, 0, sizeof(book));
        memset(cnt, 0, sizeof(cnt));
        scanf("%d", &n);
        for(int i = 2; i <= n; i++)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            g[u].push_back(v);
            book[v] = 1;
        }
        int r;
        for(int i = 1; i <= n; i++)
            if(!book[i])
            {
                r = i;
                dfs(i);
                break;
            }
        printf("Case %d: %lld\n", ca++, dp[r]);
    }
    return 0;
}


你可能感兴趣的:(DP,组合数学)