題目描述:
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,所以用組合數...