1. 程式人生 > >[國家集訓隊] Crash 的文明世界

[國家集訓隊] Crash 的文明世界

不錯的樹形$ DP$的題

可為什麼我自帶大常數啊$ cry$

連結:here


題意:給定一棵$ n$個節點的樹,邊權為$ 1$,對於每個點$ x$求$ \sum\limits_{i=1}^n dist(x,i)^k$

資料範圍:$ n<=50000,k<=150$


$ Solution$

直接推式子

上下$ DP$先考慮子樹內的貢獻

有$ in(x)^k=\sum\limits (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=\sum\limits C_{in(son[x])+1}^k=\sum\limits 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('\n');} 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; }