zoukankan      html  css  js  c++  java
  • BZOJ2159 Crash的文明世界——树上DP&&第二类Stirling数

    题意

    给定一个有 $n$ 个结点的树,设 $S(i)$ 为第 $i$ 个结点的“指标值”,定义为 $S(i)=sum_{i=1}^{n}dist(i,j)^k$,$dist(i, j)$ 为结点 $i$ 到结点 $j$ 的最小距离。请输出每个结点的指标值。($n leq 5000, k leq 150$)

    分析

    一个常用的转化

    $$n^k=sum_{i=0}^{k}S(k,i) imes C(n,i) imes i!$$

    证明可以考虑组合意义,等式的左边就是把 $k$ 个球放在 $n$ 个盒子里;右边就是枚举非空盒子的数量 $i$,注意到这里的盒子是不同的,所以还要乘上一个 $i!$。

    利用上面写的那个常用的转化。令 $dp[i][j]=sumlimits_{k=1}^{n}C(dist(i,k),j)$,那么答案为 $ans_i$就可以表示成 $ans_i=sumlimits_{j=1}^{k}S(k,j) imes j! imes dp[i][j]$。注意到 $dp[i][j]$ 是组合数是可以直接转移的,具体如下:

     

    From: 链接

    #include<cstdio>
    #include<iostream>
    using namespace std;
    int n,k,head[50010];
    const int mod=10007;
    struct edg{
        int to,next;
    }e[100010];
    int S[200][200],mi[200];
    int size,fd[50010][160],fu[50010][160];
    void add(int x,int y){size++;e[size]={y,head[x]};head[x]=size;}
    void dfs1(int x,int fa)
    {
        fd[x][0]=1;
        for (int i=head[x];i;i=e[i].next)
        {
            int y=e[i].to;
            if (y==fa) continue;
            dfs1(y,x);
            for (int j=0;j<=k;j++)
            {
                if (j)
                    fd[x][j]=(fd[x][j]+fd[y][j]+fd[y][j-1])%mod;
                else fd[x][j]=(fd[x][j]+fd[y][j])%mod;
            }
        }
    }
    void dfs2(int x,int fa)
    {
        if (fa)
        {
            for (int i=0;i<=k;i++)
            {
                if (i)
                {
                    fu[x][i]=(fu[x][i]+fu[fa][i]+fu[fa][i-1])%mod;
                    fu[x][i]=(fu[x][i]+fd[fa][i]+fd[fa][i-1])%mod;
                    fu[x][i]=(fu[x][i]-(fd[x][i]+fd[x][i-1])%mod+mod)%mod;
                    fu[x][i]=(fu[x][i]-fd[x][i-1]+mod)%mod;
                    if (i>1) fu[x][i]=(fu[x][i]-fd[x][i-2]+mod)%mod;
                }
                else fu[x][0]=n-fd[x][0];
            }
        }
        for (int i=head[x];i;i=e[i].next)
        {
            int y=e[i].to;
            if (y==fa) continue;
            dfs2(y,x);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        mi[0]=1;for (int i=1;i<=k;i++) mi[i]=mi[i-1]*i%mod;
        S[0][0]=1;
        for (int i=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;
        for (int x,y,i=1;i<n;i++)
          scanf("%d%d",&x,&y),add(x,y),add(y,x);
        dfs1(1,0);dfs2(1,0);
        for (int i=1;i<=n;i++)
        {
            int ans=0;
            for (int j=1;j<=k;j++)
                ans=(ans+1ll*S[k][j]*mi[j]*(fd[i][j]+fu[i][j]))%mod;
            printf("%d
    ",ans);
        }
    }

    参考链接:

    1. https://blog.csdn.net/Charlie_jilei/article/details/79922722

    2. https://shichengxiao01.github.io/2018/02/17/第二类斯特林数小结

  • 相关阅读:
    70.BOM
    69.捕获错误try catch
    68.键盘事件
    523. Continuous Subarray Sum
    901. Online Stock Span
    547. Friend Circles
    162. Find Peak Element
    1008. Construct Binary Search Tree from Preorder Traversal
    889. Construct Binary Tree from Preorder and Postorder Traversal
    106. Construct Binary Tree from Inorder and Postorder Traversal
  • 原文地址:https://www.cnblogs.com/lfri/p/11564356.html
Copyright © 2011-2022 走看看