The shy来全dp了
此题一眼树形dp,但很容易被误导
- 此题 状态转移 跟树的结点位置、形状没有关系,跟树的 dfs序 有关
设 d p [ i ] [ j ] dp[i][j] dp[i][j] 为选取 前 i 个 结点,使用了 前 j 种 颜色的方案数;
对于结点 i ,不需要考虑它的编号,只需要考虑它是转移中的第几个结点,从 dfs序 去转移;
有:
- d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − 1 ] ∗ ( k − j + 1 ) dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(k-j+1) dp[i][j]=dp[i−1][j]+dp[i−1][j−1]∗(k−j+1)
d p [ i ] [ j ] + = d p [ i − 1 ] [ j − 1 ] ∗ ( k − j + 1 ) dp[i][j] +=dp[i-1][j-1]*(k-j+1) dp[i][j]+=dp[i−1][j−1]∗(k−j+1) 我们很好理解,若前 i - 1 个结点选取的颜色都与当前结点的不同,当前结点能选择的颜色数量就是 总 数 k − ( j − 1 ) 总数k - (j-1) 总数k−(j−1)
若当前点选择的颜色在前 i - 1 个结点选取颜色的集合中,这里的转移就比较巧妙了;对于 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 的所有方案,当前点的颜色都只能选一种,即它的子节点的颜色,而它子节点颜色的所有情况方案都在 d p [ i − 1 ] [ j ] dp[i-1][j] dp[i−1][j] 中,所以直接转移即可。
反正就是玄学
C o d e : Code: Code:
#include<bits/stdc++.h>
#include<unordered_map>
#define mem(a,b) memset(a,b,sizeof a)
#define cinios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
#define sca scanf
#define pri printf
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define rfor(a,b,c) for(int a=b;a>=c;a--)
#define endl "\n"
#define xx first
#define yy second
//[博客地址]:https://blog.csdn.net/weixin_51797626?t=1
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
const int N = 310, M = 1000010, MM = N;
int INF = 0x3f3f3f3f, mod = 1e9 + 7;
ll LNF = 0x3f3f3f3f3f3f3f3f;
int n, m, k, T, S, D;
ll dp[N][N];
//dp[i][j]:前i个节点从k种颜色选择j种染树的方案数量
int main() {
cinios;
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;
}
}
ll ans = 0;
for (int i = 1; i <= k; i++) {
ans += dp[n][i];
ans %= mod;
}
cout << ans;
return 0;
}
/*
*/