HDU-4625 JZPTREE (树上dp,第二类斯特林数)

题目链接:HDU-4625 JZPTREE

题意

给出$n$个结点的一棵树,对每一个点$x$求所有点到$x$的距离的$k$次方之和。$1\leq n\leq 50000, 1\leq k\leq 500$。


思路

用$Tree_x$表示这棵树以$x$为根,$f(x,k)$表示所有点到$x$的距离的$k$次方之和,$dis(x,y)$表示结点$x$和$y$之间的距离,题意即求:
$$
f(x,k) = \sum_{y\in Tree_x}{dis(x,y)^k} \tag{1}
$$
我们要将之转换为能在树上做递推的递推式,并且时间复杂度在可接受的$O(nk)$范围之内。

《具体数学》中有这样的等式:
$$
n^k=\sum_{i=1}^k{n\choose i}\times S_2(k,i)\times i! \tag{2}
$$
其中$S_2(n,k)$为第二类斯特林数,表示把$n$个不同的球放到$k$个相同的盒子里,并且盒子不能为空,有多少种方案数,显然有:
$$
S_2(n,k)=S_2(n-1,k-1)+S_2(n-1,k)\times k \tag{3}
$$
式子 (2) 的意义是:把$k$个不同的球放到$n$个不同的盒子里,显然有$n^k$种方法。或者先决定放到哪$1\leq i \leq k$个盒子里,再乘以$S_2(k,i)$就表示把$k$个不同的球放到这$i$个相同的盒子里的方案数,最后乘以阶乘表示放到不同的盒子里。

结合式子 (1) (2),有:
$$
\begin{split}
f(x,k) &= \sum_{y\in Tree_x}{dis(x,y)^k} \\
&= \sum_{y\in Tree_x}\sum_{i=1}^k{dis(x,y)\choose i}\times S_2(k,i) \times i! \\
&= \sum_{i=1}^k S_2(k,i)\times i!\times \sum_{y\in Tree_x}{dis(x,y)\choose i}
\end{split} \tag{4}
$$
记$son_x$表示与$x$邻接的结点,令:
$$
\begin{split}
dp(x,i) &=& \sum_{y\in Tree_x}{dis(x,y)\choose i} \\
&=& \sum_{y\in son_x}\sum_{z\in Tree_y}{dis(y,z)+1\choose i}
\end{split} \tag{5}
$$
而组合数有如下的帕斯卡等式:
$$
{n\choose k} = {n-1 \choose k} + {n-1\choose k-1} \tag{6}
$$
结合式子 (5) (6) ,有
$$
\begin{split}
dp(x,i) &=& \sum_{y\in son_x}\sum_{z\in Tree_y}\left[ {dis(y,z)\choose i} + {dis(y,z)\choose i-1}\right] \\
&=& \sum_{y\in son_x}[dp(y,i)+dp(y,i-1)]
\end{split} \tag{7}
$$
这就是一个可以在树上递推的递推式,而且能在$O(nk)$的时间复杂度内处理出所有的$dp(x,i)$。

结合式子 (4) (5),有
$$
f(x,k) = \sum_{i=1}^k S_2(k,i)\times i!\times dp(x,i) \tag{8}
$$
因为$S_2(k,i)\times i!$是可以$O(k^2)$预处理的,所以总的时间复杂度为$O(k^2+nk)$。

(1)式是基于以$x$为树根的,代码实现中我们不能把每个结点都作为树根跑$n$次dfs,这样时间复杂度就不是$O(nk)$了。固定一个根,一次dfs处理每个结点子树部分,再跑一次dfs处理父亲结点那个分支即可。


代码实现

#include <cstdio>
#include <cstring>
const int N = 50010, K = 510, mod = 10007;
struct Edge
{
    int to, nex;
} edge[N<<1];
int fac[K], s[K][K], dp[N][K], head[N];
int cnt_e;
void add_edge(int u, int v) {
    edge[++cnt_e].to = v;
    edge[cnt_e].nex = head[u];
    head[u] = cnt_e;
}
void init1() {
    for (int i = fac[0] = 1; i < K; i++) fac[i] = fac[i-1] * i % mod;
    for (int i = s[0][0] = 1; i < K; i++) for (int j = 1; j <= i; j++) s[i][j] = (s[i-1][j-1] + j * s[i-1][j]) % mod;
}
void init2() {
    cnt_e = 0;
    memset(head, 0, sizeof(head));
}
void dfs1(int x, int fa, int k) {
    memset(dp[x], 0, sizeof(int) * (k + 1));
    dp[x][0] = 1;
    for (int i = head[x], y; y = edge[i].to, i; i = edge[i].nex) {
        if (y ^ fa) {
            dfs1(y, x, k);
            dp[x][0] += dp[y][0];
            for (int j = 1; j <= k; j++) dp[x][j] += dp[y][j] + dp[y][j-1];
        }
    }
    for (int i = 1; i <= k; i++) dp[x][i] >= mod ? dp[x][i] %= mod : 0;
}
int fa_dp(int x, int y, int i) {
    return dp[x][i] - dp[y][i] - (i > 0 ? dp[y][i-1] : 0) + 2 * mod;
}
void dfs2(int x, int fa, int k) {
    for (int i = head[x], y; y = edge[i].to, i; i = edge[i].nex) {
        if (y ^ fa) {
            for (int j = k; j > 0; j--) (dp[y][j] += fa_dp(x, y, j) + fa_dp(x, y, j - 1)) %= mod;
            dp[y][0] = dp[x][0];
            dfs2(y, x, k);
        }
    }
}
int get_ans(int x, int k) {
    long long res = 0ll;
    for (int i = 1; i <= k; i++) res += 1ll * fac[i] * s[k][i] * dp[x][i];
    return res % mod;
}

int main() {
    init1();
    int T;
    scanf("%d", &T);
    while (T--) {
        init2();
        int n, k;
        scanf("%d %d", &n, &k);
        for (int i = 0, u, v; i < n - 1; i++) {
            scanf("%d %d", &u, &v);
            add_edge(u, v), add_edge(v, u);
        }
        dfs1(1, 0, k);
        dfs2(1, 0, k);
        for (int i = 1; i <= n; i++) printf("%d\n", get_ans(i, k));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/kangkang-/p/11418904.html

时间: 2024-10-12 19:14:52

HDU-4625 JZPTREE (树上dp,第二类斯特林数)的相关文章

BZOJ 2159: Crash 的文明世界(树形dp+第二类斯特林数+组合数)

题意: 给定一棵 \(n\) 个点的树和一个常数 \(k\) , 对于每个 \(i\) , 求 \[\displaystyle S(i) = \sum _{j=1} ^ {n} \mathrm{dist}(i, j)^k\] \(n ≤ 50000, k ≤ 150\) 题解 : 先划划那个 \(S(i)\) 的式子 我们需要知道一个化 \(x^n(n \ge 0)\) 的东西qwq \[\displaystyle x^n=\sum_{k=0}^{n}\begin{Bmatrix} n \\ k

HDU 4045 Machine scheduling (第二类斯特林数+DP)

A Baidu's engineer needs to analyze and process large amount of data on machines every day. The machines are labeled from 1 to n. On each day, the engineer chooses r machines to process data. He allocates the r machines to no more than m groups ,and

hdu 2512 一卡通大冒险(第二类斯特林数)

递推思路如下,i张卡片分成j堆,那么分为两种情况:第i张卡片自成一堆或没有自成一堆. 那么自成一堆的话就是dp[i-1][j-1]种情况 不自成一堆的话就是就能在j堆种任意挑一堆放入,所以有dp[i-1][j]*j种情况 综上,如下: dp[i][j]=dp[i-1][j]*j+dp[i-1][j-1]. 关于第二类斯特林数,百度就好. 具体代码 #include <iostream> using namespace std; int dp[2005][2005]; int main() {

P4827 [国家集训队] Crash 的文明世界(第二类斯特林数+树形dp)

传送门 对于点\(u\),所求为\[\sum_{i=1}^ndis(i,u)^k\] 把后面那堆东西化成第二类斯特林数,有\[\sum_{i=1}^n\sum_{j=0}^kS(k,j)\times j!\times{dis(i,u)\choose j}\] \[\sum_{j=1}^nS(k,j)\times j!\sum_{i=0}^k{dis(i,u)\choose j}\] 于是对于每个点只要维护好\(\sum_{i=0}^k{dis(i,u)\choose j}\)就好了 因为\({n

Light OJ 1236 Race 第二类斯特林数

第二类斯特林数 n 匹马 分成1 2 3... n组 每一组就是相同排名 没有先后 然后组与组之间是有顺序的 在乘以组数的阶乘 #include <cstdio> #include <cstring> using namespace std; int dp[1010][1010]; int a[1010]; int main() { a[0] = 1; dp[0][0] = 1; for(int i = 1; i <= 1000; i++) { dp[i][0] = 0; d

Gym 101147G 第二类斯特林数

大致题意: n个孩子,k场比赛,每个孩子至少参加一场比赛,且每场比赛只能由一个孩子参加.问有多少种分配方式. 分析: k>n,就无法分配了. k<=n.把n分成k堆的方案数乘以n的阶乘.N分成k堆得方案数即第二类斯特林数 http://blog.csdn.net/acdreamers/article/details/8521134 #include <bits/stdc++.h> using namespace std; typedef long long ll; const ll

swjtu oj Paint Box 第二类斯特林数

http://swjtuoj.cn/problem/2382/ 题目的难点在于,用k种颜色,去染n个盒子,并且一定要用完这k种颜色,并且相邻的格子不能有相同的颜色, 打了个表发现,这个数是s(n, k) * k! s(n, k)表示求第二类斯特林数. 那么关键是怎么快速求第二类斯特林数. 这里提供一种O(k)的算法. 第二类斯特林数: #include <cstdio> #include <cstdlib> #include <cstring> #include <

poj 3088 组合计数,第二类斯特林数

题意:给出n个数字[1,n],问你可以组成多少种不同的序列,其中每一个序列由几个部分组成,每个部分包含几个数字,每部分内数字无序,部分之间数字有序.每个数字最多使用一次,可以不用. 思路:枚举从n个数字中选出i个数字(组合数),再枚举将这i个数字分成j个部分(第二类斯特林数),然后乘上j的全排列. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5

LightOJ 1326 - Race(第二类斯特林数啊 )

题目链接:http://lightoj.com/volume_showproblem.php?problem=1326 百度百科:斯特林数 ACdreamer:第一类Stirling数和第二类Stirling Disky and Sooma, two of the biggest mega minds of Bangladesh went to a far country. They ate, coded and wandered around, even in their holidays.