樹 DP or 組合數學

2021-10-23 04:18:35 字數 1979 閱讀 1495

題目描述:

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

輸入描述:

第一行兩個整數n,k代表點數和顏色數;

接下來n-1行,每行兩個整數x,y表示x與y之間存在一條邊;

輸出描述:

輸出乙個整數表示方案數(mod 1e9+7)。

示例1輸入

4 31 2

2 32 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(

n−1,

i−1)

a(k,

i)

\sum_^c(n - 1, i - 1)a(k , i)

i=1∑k​

c(n−

1,i−

1)a(

k,i)

時間複雜度o(n)。

**:

#include

using

namespace std;

typedef

long

long

int ll;

ll mod =

1e9+7;

ll qp

(ll a,ll b, ll p)

a =(a*a)

%p;b >>=1;

}return ans%p;

}ll inv

(ll x)

ll c

(ll n,ll m)

ll a

(ll n,ll m,ll mod)

void

solved()

cout<}int

main()

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

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 ans =0;

for(

int i =

1; i <= k; i++

) cout<}int

main()

組合數學 求組合數

對於求組合數,要根據所給資料範圍來選擇合適的演算法 這道題中所給的資料範圍適合用打表的方法直接暴力求解 先用4e6的複雜度預處理出所有的情況,再用1e4的複雜度完成詢問即可 include using namespace std const int n 2010 const int mod 1e9 ...

數學 組合數學

mod must be a prime const int mod 1e9 7 namespace combinatory ll inv ll x ll fac maxn invfac maxn void initc int n ll a ll n,ll m ll c ll n,ll m ll d ...

NOIP模擬 排列樹(組合數學)

做這道題的時候真的難受,屬於知道他考你什麼但就是不知道怎麼做,令人蛋疼啊。題意大概就是求拓撲排序的方案數,然額太菜了寫不出來。對於樹上每個節點,記錄他的size,對於根節點,他的編號一定是1,因為子節點的編號一定比父節點大,就類似於一種偏序關係,考慮每個節點分配的編號數就是他的size,所以用組合數...