組合數學+樹形dp+第二類striling數--luoguP4827 [國家集訓隊] Crash 的文明世界
阿新 • • 發佈:2018-11-25
一道組合數學的好題。
首先是
的
分暴力:
從
,用二項式定理展開每個節點維護
個數
兩次樹形
分別求子樹的和從父親上傳下來的,
表示
節點子樹到它的
次冪和。
程式碼如下:
#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;
}
關於求 次冪的計算,可以轉化成求組合數
首先有一個公式:
其中
表示第二類
數,有遞推公式:
這樣的話
和
是不變的都可以預處理出來,然後只需要考慮組合數的變化,因為
,所以可以維護
表示子樹中
,父親上面的同理,然後樹形
維護
#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