不知道怎麼稱呼的題目:樹形DP計數
題意:給出1棵樹(n<=300),以及有k(<=300)種顏色。現在要給每個節點上色,滿足:任意兩個相同顏色的點x,y,其簡單路徑上的點必須也是這個顏色。求方案總數。
題解:首先,染色規則其實說明了一件事情:相同顏色的點是一個塊。那麼我們只需要求出這棵樹用1/2/3/……/k種顏色染色的方案數,然後分別乘上A(k,1)/A(k,2)/A(k,3)/……/A(k,k)就行了。那麼比較明顯這就是一個樹形DP了。用dp[ node ][ kk ]表示node以及node以下的點,用kk種顏色染色的方案數,其中有:父節點的顏色號不小於子節點的顏色號。(因為組合數是最後乘上的,所以這一步只需要每種方案中,每個點有自己的標號就可以,至於怎麼組合最後在考慮)
設node為temp的父親 轉移方程式: dp ' [ i+j ] += dp[ node ][ i ]*dp[ temp ][ j ] (temp和node點的顏色不同,這樣總共就有i+j種顏色了。dp[ node ][ i ]統計的每一種方案而言,算上dp[ temp ][ j ]的每種方案,都會構成一個唯一的新方案,至於標號不需要考慮,只要顏色方案確定了,標號必然確定。)dp ‘ [ i+j-1 ] += dp[ node ][ i ]*dp[ temp ][ j ](temp和node顏色相同。)然後再把dp ’ 陣列重新賦給dp[ node ]。因為當多了一個孩子的時候,原來dp[ node ]統計的方案數量 由於沒有考慮新孩子,都變得不合法了,新算的這個dp ' 才是合法的。
初始條件是dp[ i ][ 1 ] =1意思是 i 這個點染上一個“某種顏色”的方案數=1,這個“某種顏色”在具體計算dp[ i ]的時候,會根據情況得到相應的意義。比如葉子節點dp[ i ][ 1 ]=1表示用1種顏色染掉 i 及其孩子(不存在的)有一種方案。i 的 爹爹是 j 。dp[ j ][ 1 ] =1,根據轉移方程:i+j那種情況表示“某種顏色”不同於 i 的。i+j-1那種情況下表示 “某種顏色”和 i 的相同。。。。。。囉裡囉唆囉裡囉唆。。。
注意:牛客網上邊的LL佔位符不是I64d
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int MAX = 305; const LL MOD = 1e9+7; vector<int> E[MAX]; LL dp[MAX][MAX]; LL f[MAX]; LL C[MAX][MAX]; LL A[MAX]; int n,k; int u,v; void input(){ scanf("%d%d",&n,&k); for (int i=1;i<n;i++){ scanf("%d%d",&v,&u); E[v].push_back(u); E[u].push_back(v); } memset(dp,0,sizeof dp); } void dfs(int node,int father){ if (E[node].size()==1&&node!=1){ dp[node][1] = 1; return; } dp[node][1]=1; for (int temp:E[node]){ if (temp==father){ continue; } dfs(temp,node); memset(f,0,sizeof f); for (int i=1;i<=k;i++){ for (int j=1;j<=k;j++){ if (i+j<=k){ f[i+j]+=dp[temp][j]*dp[node][i]; f[i+j]%=MOD; } if (i+j-1<=k&&i+j-1>0) f[i+j-1]+=dp[temp][j]*dp[node][i]; f[i+j-1]%=MOD; } } for (int i=1;i<=k;i++){ dp[node][i] = f[i]; } } } void print(){ for (int i=1;i<=n;i++){ for (int j=1;j<=k;j++){ printf("dp[%d][%d]=%d\n",i,j,dp[i][j]); } } } void solve(){ dfs(1,0); LL ans =0; for (int i=1;i<=k;i++){ ans+=(1LL*dp[1][i]*C[k][i]%MOD*A[i]%MOD); ans%=MOD; } // print(); cout<<ans<<endl; } void init(){ C[1][0]=1; C[1][1]=1; for (int i=2;i<=300;i++){ C[i][0]=1; C[i][i]=1; for (int j=1;j<i;j++){ C[i][j] = C[i-1][j-1]+C[i-1][j]; C[i][j]%=MOD; } } A[1]=1; A[0]=1; for (int i=2;i<=300;i++){ A[i] = A[i-1]*i; A[i]%=MOD; } } int main(){ init(); input(); solve(); return 0; }
,還是用cout吧。為什麼不開LL讓自己WA一次啊。。。為什麼中間沒有膜又讓自己WA一次啊。。
Code: