• [HEOI2013]SAO


    可以发现题目给的是一颗有向树的形式,不论是有向树还是无向树,我们考虑 (dp) 转移基本是从子树转移到当前的父亲,状态也一般涉及的是在子树内部的状态,本题也不例外。

    我们钦定 (1) 为有向树的根,首先可以发现限制每个点位置的那些点一定是这个点在有向树上的儿子或者是父亲,并且只要确定限制它点的位置之后我们就能知道它能放的位置,因此我们就可以考虑令 (dp_{i, j}) 表示在以 (i) 为根的满足子树要求的序列(下面简称子树序列)中 (i) 的位置在 (j) 的方案数。对于 (u ightarrow v) 的边 (v)(u) 在有向树上的儿子,转移的化我们可以考虑枚举 (v) 所在其子树序列的位置 (k),以及 (k) 后面有 (l) 个位置插入到 (j) 之前,那么就有转移:

    [dp_{u, j + k} = dp_{u, j} imes sumlimits_{k = 1} ^ {s_v} dp_{v, k} sumlimits_{l = k} ^ {s_v} dbinom{l + j - 1}{j - 1} imes dbinom{s_v - l + s_u - j}{s_u - j} ]

    其中 (s_i) 表示以 (i) 为根的子树大小。上面的那个 (dp) 后面那一坨计算的东西貌似和 (k) 是没有关系的,可以考虑先枚举 (l) 试试:

    [dp_{u, j + k} = dp_{u, j} imes sumlimits_{k = 1} ^ {s_v} dbinom{k + j - 1}{j - 1} imes dbinom{s_v - k + s_u - j}{s_u - j} imes sumlimits_{l = 1} ^ k dp_{v, l} ]

    后面那一坨显然可以使用前缀和优化掉,这个 (dp) 的复杂度其实是 (O(n ^ 2)) 的,在 [HAOI2015]树上染色 提到了证明方法。

    同理我们可以得到 (v ightarrow u),其中 (v)(u) 的儿子的转移方程:

    [dp_{u, j + k} = dp_{u, j} imes sumlimits_{k = 0} ^ {s_v - 1} dbinom{k + j - 1}{j - 1} imes dbinom{s_v - k + s_u - j}{s_u - j} imes sumlimits_{l = k + 1} ^ {s_v} dp_{v, l} ]

    一样的记录前缀优化可以做到 (O(n ^ 2))

    一些坑点:

    • 以后树形背包直接使用滚动数组记录这一位和上一位的答案这样就不用纠结枚举顺序了。
    #include<bits/stdc++.h>
    using namespace std;
    #define N 1000 + 5
    #define Mod 1000000007
    #define rep(i, l, r) for(int i = l; i <= r; ++i)
    #define dep(i, l, r) for(int i = r; i >= l; --i)
    #define Next(i, u) for(int i = h[u]; i; i = e[i].next)
    struct edge{
        int v, next, w;
    }e[N << 1];
    char opt[N];
    int T, n, u, v, tot, h[N], s[N], dp[2][N][N], C[N][N], S[N][N];
    int read(){
        char c; int x = 0, f = 1;
        c = getchar();
        while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
        return x * f;
    }
    void add(int u, int v, int w){
        e[++tot].v = v, e[tot].w = w, e[tot].next = h[u], h[u] = tot;
    }
    int Inc(int a, int b){
        return (a += b) >= Mod ? a - Mod : a;
    }
    int Dec(int a, int b){
        return (a -= b) < 0 ? a + Mod : a;
    }
    int Mul(int a, int b){
        return 1ll * a * b % Mod;
    }
    void dfs(int u, int fa){
        s[u] = 1, dp[0][u][1] = 1; int p = 1;
        Next(i, u){
            int v = e[i].v; if(v == fa) continue;
            dfs(v, u);
            if(!e[i].w){
                rep(j, 1, s[u]) rep(k, 1, s[v]){
                    int tmp = Mul(C[k + j - 1][j - 1], C[s[v] - k + s[u] - j][s[u] - j]);
                    dp[p][u][j + k] = Inc(dp[p][u][j + k], Mul(tmp, Mul(dp[p ^ 1][u][j], S[v][k])));
                }
            }
            else{
                rep(j, 1, s[u]) rep(k, 0, s[v] - 1){
                    int tmp = Mul(C[k + j - 1][j - 1], C[s[v] - k + s[u] - j][s[u] - j]);
                    dp[p][u][j + k] = Inc(dp[p][u][j + k], Mul(tmp, Mul(dp[p ^ 1][u][j], Dec(S[v][s[v]], S[v][k]))));
                }
            }
            p ^= 1;
            rep(j, 1, s[u]) dp[p][u][j] = 0;
            s[u] += s[v];
        }
        rep(i, 1, s[u]) S[u][i] = Inc(S[u][i - 1], dp[p ^ 1][u][i]);
    }
    int main(){
        T = read();
        rep(i, 0, N - 5) C[i][0] = 1;
        rep(i, 1, N - 5) rep(j, 1, i) C[i][j] = Inc(C[i - 1][j - 1], C[i - 1][j]);
        while(T--){
            n = read(), tot = 0;
            memset(h, 0, sizeof(h)), memset(dp, 0, sizeof(dp));
            rep(i, 1, n - 1){
                u = read() + 1, scanf("%s", opt + 1), v = read() + 1;
                if(opt[1] == '>') add(u, v, 0), add(v, u, 1);
                else add(u, v, 1), add(v, u, 0);
            }
            dfs(1, 0);
            printf("%d
    ", S[1][n]);
        }
        return 0;
    }
    
    GO!
  • 相关阅读:
    hadoop基础学习---数据管理策略
    hadoop基础学习---基本概念
    hadoop配置
    linux配置java环境
    Linux使用expect实现自动登录的脚本
    机器学习系列-寒小阳
    深度学习与计算机视觉系列-寒小阳
    深度学习与计算机视觉(12)_tensorflow实现基于深度学习的图像补全
    深度学习与计算机视觉(11)_基于deep learning的快速图像检索系统
    深度学习与计算机视觉系列(10)_细说卷积神经网络
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13486189.html
Copyright © 2020-2023  润新知