zoukankan      html  css  js  c++  java
  • 洛谷 P4099 [HEOI2013]SAO 题解

    一、题目:

    洛谷原题

    二、思路:

    考虑树形 DP。

    DP 状态为 (f[x,k]) 表示在 (x) 这棵子树中,(x) 的拓扑序的位置是 (k) 的方案数。

    那么设接下来考虑的儿子是 (y)(tmpf[k]) 表示考虑了 (y) 之前的儿子,(x) 的拓扑序的位置是 (k) 的方案数。

    假设当前我们要用 (tmpf[p_1])(f[y,p_2]) 去更新 (f[x,p_3])

    那么分为两种情况。

    1. (x) 的拓扑序在 (y) 之前。

      1. 首先来确定 (p_3) 的范围。我们会发现,(p_3) 至少是 (p_1);又因为 (y) 的子树中有 (p_2-1) 个节点既可以排在 (x) 前,也可以排在 (x) 后,所以 (p_3) 最大是 (p_1+p_2-1)。即 (p_1leq p_3leq p_1+p_2-1)

      2. 接下来考虑转移。在 (p_3) 前面,有 (p_3-1) 个空位,选出 (p_1-1) 个来让 (x) 原来的那些拓扑序在 (x) 之前的子孙填;在 (p_3) 后面,有 (siz_x+siz_y-p_3) 个空位,选出 (siz_x-p_1) 个来让 (x) 原来的那些拓扑序在 (x) 之后的子孙填。

        [f[x,p_3]overset{+}gets dbinom{p_3-1}{p_1-1}dbinom{siz_x+siz_y-p_3}{siz_x-p_1}tmpf[p_1] imes f[y,p_2] ]

        有些同学可能会问,你只决定了这些点的位置就可以确定整棵子树的拓扑序吗?答案是肯定的。试想,(x) 原来的那些子孙的在拓扑序中相对位置是已经确定下来的,(y) 的子孙也是。那么我们填完 (x) 原本的子孙后,从第一位依次向后找空位,先将 (y) 的子树中排在 (y) 之前的 (p_2-1) 个子孙填满,再将后 (siz_y-p_2) 个子孙填满。也就是说,(x) 的子孙一旦确定了位置,整棵子树的拓扑序也就相应确定了。

    2. (x) 的拓扑序在 (y) 之后。

      1. (p_1+p_2leq p_3leq p_1+siz_y)
      2. 转移同上。

    注意到转移中只有一处出现了 (p_2),所以可以对 (f[y,p_2]) 使用前缀和优化。具体实现见代码。

    三、代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    #define FILEIN(s) freopen(s, "r", stdin)
    #define FILEOUT(s) freopen(s, "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    inline int read(void) {
        int x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return f * x;
    }
    
    const int MOD = 1000000007, MAXN = 1005;
    
    int n, head[MAXN], tot;
    int siz[MAXN];
    long long f[MAXN][MAXN], tmpf[MAXN];
    long long C[MAXN][MAXN];
    
    struct Edge {
        int y, next, w;
        Edge() {}
        Edge(int _y, int _next, int _w) : y(_y), next(_next), w(_w) {}
    }e[MAXN << 1];
    
    inline void connect(int x, int y, int w) {
        e[++ tot] = Edge(y, head[x], w);
        head[x] = tot;
    }
    
    void dfs(int x, int fa) {
        siz[x] = 1;
        f[x][1] = 1;
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].y;
            if (y == fa) continue;
            dfs(y, x);
    
            memcpy(tmpf, f[x], sizeof tmpf);
            mem(f[x], 0);
    
            if (e[i].w == 0) {
                for (int p1 = 1; p1 <= siz[x]; ++ p1) {
                    for (int p3 = p1; p3 <= p1 + siz[y] - 1; ++ p3) {
                        (f[x][p3] += C[p3 - 1][p1 - 1] * C[siz[x] + siz[y] - p3][siz[x] - p1] % MOD * tmpf[p1] % MOD * (f[y][siz[y]] - f[y][p3 - p1]) % MOD) %= MOD;
                        if (f[x][p3] < 0) f[x][p3] += MOD;
                    }
                }
            }
            else {
                for (int p1 = 1; p1 <= siz[x]; ++ p1) {
                    for (int p3 = p1 + 1; p3 <= p1 + siz[y]; ++ p3) {
                        (f[x][p3] += C[p3 - 1][p1 - 1] * C[siz[x] + siz[y] - p3][siz[x] - p1] % MOD * tmpf[p1] % MOD * f[y][p3 - p1] % MOD) %= MOD;
                    }
                }
            }
    
            siz[x] += siz[y];
        }
        for (int p3 = 2; p3 <= siz[x]; ++ p3) (f[x][p3] += f[x][p3 - 1]) %= MOD;
    }
    
    inline void prework(void) {
        C[0][0] = 1;
        for (int i = 1; i <= 1000; ++ i) {
            C[i][0] = 1;
            for (int j = 1; j <= i; ++ j)
                C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
        }
    }
    
    int main() {
        prework();
        int T = read();
        while (T --) {
            tot = 0; mem(head, 0); mem(f, 0); mem(siz, 0);
            n = read();
            for (int i = 1; i < n; ++ i) {
                int x = read() + 1; char op[3]; scanf("%s", op); int y = read() + 1;
                connect(x, y, (*op) == '>');
                connect(y, x, (*op) == '<');
            }
            dfs(1, 0);
            printf("%lld
    ", f[1][siz[1]]);
        }
        return 0;
    }
    

  • 相关阅读:
    9.1 Dubbo和Zookeeper安装
    9.0 dubbo与zookeeper的关系
    8. MVC三层架构到微服务架构的思考
    7.6 SpringBoot读取Resource下文件的几种方式
    7.5 cron表达式详解,cron表达式写法,cron表达式例子
    7.4 异步、定时和邮件发送任务
    7.3.2 Swagger注解
    springboot自定义消息转换器HttpMessageConverter
    SpringBoot项目中获取applicationContext对象
    为什么要实现Serializable
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14964888.html
Copyright © 2011-2022 走看看