• CF954H Path Counting


    一开始的想法是枚举路径的 ( m LCA) 然后再枚举两边的深度,但是这样无论如何我都只能做到 (O(n ^ 3)) 的复杂度。

    只能考虑换一种方式计数,注意到点分治可以解决树上一类路径问题,于是我们考虑使用类似点分治的方式对树上路径计数。

    具体地,我们考虑计算以 (i) 为端点的路径数量,那么就会存在两种路径:向上走的和向下走的,分别令其为 (f, g)

    因为深度相同的点都是等价的,因此我们将状态设置为: (f_{i, j}) 表示从 (i) 开始往上长度为 (j) 的路径数,对 (g) 类似。

    那么你会发现 (g) 是非常好求的,因为往下走就只可能一直往儿子走,本质上就是往下走 (j) 层后的点的数量,那么可知:

    [g_{i, j} = prod_{k = 1} ^ j a_{i + k - 1} = g_{i, j - 1} imes a_{i + j - 1} ]

    再来考虑如何求 (f),可以发现 (i) 往上走必然会经过 (fa_i),之后又分为两种情况:(fa_i) 往下走或 (i) 继续往上走 (j - 1) 步,那么有转移:

    [f_{i, j} = f_{i - 1, j - 1} + g_{i - 1, j - 1} ]

    但是需要减去 (fa_i) 往下走到 (i) 的方案数,即:

    [f_{i, j} = f_{i - 1, j - 1} + g_{i - 1, j - 1} - g_{i, j - 2} ]

    特别的,当 (f_{i, 0}, g_{i, 0}) 我们只能钦定一个为 (1),否则在计算 (f_{i, 1}) 时会记重,为了方便 (g) 的计算,我们令 (g_{i, 0} = 1, f_{i, 0} = 0)

    最终求答案的时候你会认为答案是 (sumlimits_{i = 1} ^ n a_i imes (f_{i, k} + g_{i, k})),但是可以发现同一条路径会在两个端点处被计算一次,因此答案还需减半。

    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i, l, r) for (int i = l; i <= r; ++i)
    const int N = 10000 + 5;
    const int M = 5000 + 5;
    const int Mod = 1e9 + 7;
    int n, inv, cur, a[N], b[N], ans[N], f[2][N], g[M][M];
    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; 
    }
    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;}
    int fpow(int a, int b) { int ans = 1; for (; b; a = Mul(a, a), b >>= 1) if(b & 1) ans = Mul(ans, a); return ans;}
    int main() {
        n = read(), inv = fpow(2, Mod - 2);
        b[1] = 1;
        rep(i, 1, n - 1) a[i] = read(), b[i + 1] = Mul(b[i], a[i]);
        rep(i, 1, n) {
            g[i][0] = 1;
            rep(j, 1, n - i) g[i][j] = Inc(g[i][j], Mul(g[i][j - 1], a[i + j - 1]));
        }
        f[0][0] = cur = 1;
        rep(i, 2, n) {
            rep(j, 1, 2 * n - 2) f[cur][j] = 0;
            f[cur][0] = 1;
            rep(j, 1, 2 * n - 2) {
                f[cur][j] = Inc(f[cur][j], f[cur ^ 1][j - 1]);
                if(j - 1 <= n && j - 1 > 0) f[cur][j] = Inc(f[cur][j], Dec(g[i - 1][j - 1], g[i][j - 2]));
            }
            rep(j, 1, 2 * n - 2) ans[j] = Inc(ans[j], Mul(Mul(Inc(f[cur][j], (j < i)), b[i]), inv));
            cur ^= 1;
        }
        rep(i, 1, 2 * n - 2) printf("%d ", ans[i]);
        return 0;
    }
    

    从本题当中可以看出书上路径计数的两种方法:

    1. 通过枚举 ( m LCA),当要求的路径特征与端点无太多关系时常用。

    2. 通过枚举端点,然后运用类似点分治的方式递推,多用于点对之间有要求的问题,例如本题。

  • 相关阅读:
    jq获取img高度(动态生成的image高度为0原因)
    Idea集成使用SVN教程
    RPC框架pigeon源码分析
    java多线程面试题整理及答案
    深入理解JVM线程模型
    dubbo 2.8.4(dubbox)的jar包制作【添加到maven本地仓库】
    【学习】027 Dubbo
    【学习】026 Zookeeper
    【学习】025 RocketMQ
    【学习】024 springCloud
  • 原文地址:https://www.cnblogs.com/Go7338395/p/13870504.html
Copyright © 2020-2023  润新知