组合数学+树形dp+第二类striling数--luoguP4827 [国家集训队] Crash 的文明世界

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sizeof_you/article/details/84405407

传送门

一道组合数学的好题。

首先是 n k 2 nk^2 50 50 分暴力:
x k > ( x + 1 ) k x^k->(x+1)^k ,用二项式定理展开每个节点维护 k + 1 k+1 个数
两次树形 d p dp 分别求子树的和从父亲上传下来的, f [ u ] [ j ] f[u][j] 表示 u u 节点子树到它的 j j 次幂和。
代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 50005
using namespace std;
const int mod=10007;

inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}

int n,k,cnt,head[N],f[N][151],g[N][151],h[N][151],C[151][151];

struct EDGE{
    int to,nxt;
}edge[N<<1];
inline void add(int x,int y){
    edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}

inline void prework(){
    for(int i=0;i<=k;i++) C[i][0]=1;
    for(int i=1;i<=k;i++)
        for(int j=1;j<=k;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}

void dfs1(int u,int fa){
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==fa) continue;
        dfs1(v,u);
        for(int j=0;j<=k;j++){
            for(int l=0;l<=j;l++)
                (h[v][j]+=1LL*f[v][l]*C[j][j-l]%mod)%=mod;
            (f[u][j]+=h[v][j])%=mod;
        }
        for(int j=0;j<=k;j++) (++f[u][j])%=mod,(++h[v][j])%=mod; 
    } return;
}

void dfs2(int u,int fa){
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==fa) continue;
        for(int j=0;j<=k;j++){
            for(int l=0;l<=j;l++)
                (g[v][j]+=1LL*g[u][l]*C[j][j-l]%mod)%=mod;
            for(int l=0;l<=j;l++)
                (g[v][j]+=1LL*(f[u][l]-h[v][l]+mod)%mod*C[j][j-l]%mod)%=mod;
        }
        for(int j=0;j<=k;j++) (++g[v][j])%=mod;
        dfs2(v,u);
    } return;
}

int main(){
    n=rd(); k=rd(); 
    for(int i=1;i<n;i++){
        int x=rd(),y=rd();
        add(x,y); add(y,x);
    }
    prework();
    dfs1(1,1); dfs2(1,1);
    for(int i=1;i<=n;i++) printf("%d\n",(f[i][k]+g[i][k])%mod);
    return 0;
}

关于求 k k 次幂的计算,可以转化成求组合数

首先有一个公式:
n k = i = 0 k S ( k , i ) × i ! × C ( n , i ) n^k=\sum_{i=0}^k S(k,i)\times i!\times C(n,i)
其中 S S 表示第二类 s t r i l i n g striling 数,有递推公式:
S ( n , i ) = S ( n 1 , i ) × i + S ( n 1 , i 1 ) S(n,i)=S(n-1,i)\times i+S(n-1,i-1)
这样的话 S S i ! i! 是不变的都可以预处理出来,然后只需要考虑组合数的变化,因为 C ( n , i ) = C ( n 1 , i 1 ) + C ( n 1 , i ) C(n,i)=C(n-1,i-1)+C(n-1,i) ,所以可以维护 f [ u ] [ j ] f[u][j] 表示子树中 k C ( d i s t ( u , k ) , j ) \sum_k C(dist(u,k),j) ,父亲上面的同理,然后树形 d p dp 维护

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 50005
using namespace std;
const int mod=10007;

inline int rd(){
    int x=0,f=1;char c=' ';
    while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}

int n,k,cnt,head[N],f[N][151],g[N][151],s[151][151],C[N][151],fac[151];

struct EDGE{
    int to,nxt;
}edge[N<<1];
inline void add(int x,int y){
    edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt;
}

inline void prework(){
    for(int i=0;i<=k;i++) C[i][0]=1,s[i][i]=1; s[0][0]=0;
    for(int i=1;i<=N;i++)
        for(int j=1;j<=k;j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    for(int i=2;i<=k;i++)
        for(int j=1;j<=i;j++) s[i][j]=(s[i-1][j]*j%mod+s[i-1][j-1])%mod;
    fac[0]=1;
    for(int i=1;i<=k;i++) fac[i]=1LL*fac[i-1]*i%mod;
}

void dfs1(int u,int fa){
    f[u][0]=1;
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==fa) continue;
        dfs1(v,u); (f[u][0]+=f[v][0])%=mod;
        for(int j=1;j<=k;j++){
            (f[u][j]+=(f[v][j]+f[v][j-1])%mod)%=mod;
        }
    } return;
}

void dfs2(int u,int fa){
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v==fa) continue;
        g[v][0]=n-f[v][0];
        for(int j=1;j<=k;j++){
            g[v][j]=((g[u][j-1]+g[u][j]+f[u][j-1]+f[u][j]-2*f[v][j-1]-f[v][j])%mod+mod)%mod;
            if(j>1) g[v][j]=(g[v][j]-f[v][j-2]+mod)%mod; 
        }
        dfs2(v,u);
    } return;
}

int main(){
    n=rd(); k=rd(); 
    for(int i=1;i<n;i++){
        int x=rd(),y=rd();
        add(x,y); add(y,x);
    }
    prework();
    dfs1(1,1); dfs2(1,1);
    for(int i=1;i<=n;i++){
        int ans=0;
        for(int j=0;j<=k;j++)
            (ans+=1LL*s[k][j]*fac[j]%mod*(f[i][j]+g[i][j])%mod)%=mod;
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/84405407