樹(DP or 組合數學)

題目描述:
shy有一顆樹,樹有n個結點。有k種不同顏色的染料給樹染色。一個染色方案是合法的,當且僅當對於所有相同顏色的點對(x,y),x到y的路徑上的所有點的顏色都要與x和y相同。請統計方案數。

輸入描述:
第一行兩個整數n,k代表點數和顏色數;
接下來n-1行,每行兩個整數x,y表示x與y之間存在一條邊;

輸出描述:
輸出一個整數表示方案數(mod 1e9+7)。

示例1
輸入
4 3
1 2
2 3
2 4
輸出
39

備註:
對於30%的數據,n≤10, k≤3;
對於100%的數據,n,k≤300。

思路:
組合數學做法:
從[1,k]枚舉所有聯通塊(聯通塊滿足要求),然後求出每種聯通塊能夠顏色的方案數量。
怎麼枚舉所有聯通塊?
從邊的角度來考慮,我們每次枚舉 i 個聯通塊,就會少i - 1 條邊,那麼從n - 1條邊刪掉i - 1條邊就是它的方案數量。這個方案數與順序無關所以是C(n - 1, i - 1),然後每種方案數用k 種顏色去染 i 個聯通塊就是排列問題了,所以最終答案就是i=1kC(n1,i1)A(k,i)\sum_{i=1}^{k}C(n - 1, i - 1)A(k , i)
時間複雜度O(n)。
代碼:

#include<bits/stdc++.h>
using namespace std;

typedef long long int ll;
ll mod = 1e9 + 7;
ll qp(ll a,ll b, ll p){ll ans = 1;while(b){if(b&1){ans = (ans*a)%p;--b;}a = (a*a)%p;b >>= 1;}return ans%p;}
ll Inv(ll x)          { return qp(x,mod-2,mod);}
ll C(ll n,ll m){if (m>n) return 0;ll ans = 1;for (int i = 1; i <= m; ++i) ans=ans*Inv(i)%mod*(n-i+1)%mod;return ans%mod;}
ll A(ll n,ll m,ll mod){ll sum=1; for(int i=n;i>=n-m+1;i--) sum=(sum*i)%mod; return sum%mod;}

void solved(){
	ll n,k;cin>>n>>k;
	ll ans = 0;
	for(int i = 1; i <= k; i++){
		ans += (C(n - 1, i - 1)%mod * A(k,i,mod) % mod)%mod;
	}
	cout<<ans % mod<<endl;
}
int main(){
	solved();
	return 0;
}

DP做法:
一開始看的這個題,感覺不知道怎麼搞,一開始想的是dfs暴力搞一下,但是要檢查(u,v)顏色是不是相同就感覺寫不出來。。。
然後看了一下題解是dp,定義
dp[i][j]:前i個節點從k種顏色中取j種顏色取染色的方案數。(一開始以爲從前i個節點用j種顏色。。)
轉移方程:dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] * (k - (j - 1))
大概意思是:考慮第i個節點從k種顏色選j種的方案數會等於跟它父親節點顏色保持一致(滿足條件),或者是用新的顏色那麼就是原來父親節點用了的顏色數量 * (總顏色數量 - 父親節點用了的數量)這樣相加就是dp[i][j]的數量了。

我感覺這題好像可以用組合數學的方法做,先想一想,想出來了再更新。

#include<bits/stdc++.h>
using namespace std;
 
const long long int mod = 1000000000 + 7;
long long int  dp[10000][10000];
//dp[i][j]:前i個節點從k種顏色選擇j種染樹的方案數量
 
void solved(){
    long long int n,k;cin>>n>>k;
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= k; j++){
            dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1] * (k - (j - 1));
            dp[i][j] %= mod;
        }
    }
    long long int ans = 0;
    for(int i = 1; i <= k; i++){
        ans += dp[n][i];
        ans %= mod;
    }
    cout<<ans;
}
int main(){
    solved();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章