• [国家集训队] Crash 的文明世界


    不错的树形$ DP$的题

    可为什么我自带大常数啊$ cry$

    链接:here


    题意:给定一棵$ n$个节点的树,边权为$ 1$,对于每个点$ x$求$ sumlimits_{i=1}^n dist(x,i)^k$

    数据范围:$ n<=50000,k<=150$


    $ Solution$

    直接推式子

    上下$ DP$先考虑子树内的贡献

    有$ in(x)^k=sumlimits (in(son[x])+1)^k$

    这是因为自己子树内的每个点到自己的距离都$ ++$

    再考虑子树外的贡献

    有$ out(x)^k=(out(fa[x])+1)^k+(in(fa[x])+1)^k-(in(x)+2)^k$

    这是因为父亲节点子树外的节点和父亲子树中除自己外其他子树内的节点到自己的距离相比原先都$ ++$

    而自己这棵子树内不增反减所以再$ -=2$

    直接二项式展开是$ nk^2$的

    不过这类式子可以转化成斯特林数

    我们只要从求$ in(x)^k/out(x)^k$转化成求$ C_{in(x)/out(x)}^k$即可

    这样$ C_{in(x)}^k=sumlimits C_{in(son[x])+1}^k=sumlimits C_{in(son[x])}^k+C_{in(son[x])}^{k-1}$

    求$ out$的时候同理,唯一的区别是$ C_{in(x)+2}^k$需要展开两层

    代码还是比较清真的

    $ my code$

    #include<ctime>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #define p 10007
    #define M 100010
    #define rt register int
    #define ll long long
    using namespace std;
    inline ll read(){
        ll x = 0; char zf = 1; char ch = getchar();
        while (ch != '-' && !isdigit(ch)) ch = getchar();
        if (ch == '-') zf = -1, ch = getchar();
        while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x * zf;
    }
    void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);}
    void writeln(const ll y){write(y);putchar('
    ');}
    int i,j,k,m,n,x,y,z,cnt;
    int val[50010][155],out[50010][155],fa[50010]; 
    int F[M],L[M],N[M],a[M];
    void add(int x,int y){
        a[++k]=y;
        if(!F[x])F[x]=k;
        else N[L[x]]=k;
        L[x]=k;
    }
    void dfs(int x,int pre){
        fa[x]=pre;val[x][0]=1;
        for(rt i=F[x];i;i=N[i])if(a[i]!=pre){
            dfs(a[i],x);
            (val[x][0]+=val[a[i]][0])%=p;
        }
        out[x][0]=n-val[x][0];
    }
    void get(int x){
        for(rt i=F[x];i;i=N[i])if(a[i]!=fa[x]){
            get(a[i]);
            for(rt j=1;j<=m;j++)
            (val[x][j]+=val[a[i]][j]+val[a[i]][j-1])%=p;
        } 
    }
    int S[155][155],inv[155];
    void up(int x){
        for(rt j=1;j<=m;j++)
        if(x==1)out[x][j]=0;
        else out[x][j]=(out[fa[x]][j]+out[fa[x]][j-1]+val[fa[x]][j]+val[fa[x]][j-1]-(val[x][j-1]*2+val[x][j]+val[x][j-2]))%p;
        for(rt i=F[x];i;i=N[i])if(a[i]!=fa[x])up(a[i]);
    }
    int main(){
        n=read();m=read();
        for(rt i=1;i<n;i++){
            x=read();y=read();
            add(x,y);
            add(y,x);
        }
        dfs(1,1);get(1);up(1);
        for(rt i=1;i<=n;i++)
        for(rt j=0;j<=m;j++)val[i][j]+=out[i][j];
        S[0][0]=1;
        for(rt i=1;i<=m;i++)
        for(rt j=1;j<=i;j++)
        S[i][j]=(S[i-1][j-1]+j*S[i-1][j])%p;
        inv[0]=inv[1]=1;
        for(rt i=2;i<=m+1;i++)inv[i]=inv[p%i]*(p-p/i)%p;
        for(rt i=1;i<=n;i++){
            ll ans=0,C=1,jc=1;
            for(rt j=0;j<=m;j++){
                (ans+=val[i][j]*S[m][j]*jc)%=p;
                jc=jc*(j+1)%p;
            }
            writeln((ans+p)%p);
        }
        return 0;
    }
  • 相关阅读:
    图解ArrayList源码
    HashMap相关
    1. 两数之和
    判定字符是否唯一
    反射相关
    自定义注解
    TreeMap相关
    判定是否互为字符重排
    2. 两数相加
    线程基础
  • 原文地址:https://www.cnblogs.com/DreamlessDreams/p/9887178.html
Copyright © 2020-2023  润新知