牛客练习赛1 树 dp + dfs序

题目链接

题解:

一开始考虑树形DP自底向上更新发现儿子多的时候情况太多,无法考虑。

可以按照dfs序对树上的点一个个进行染色。这样对一个节点x进行染色的时候,它的父亲节点已经被染色了。
定义状态 d p [ i ] [ j ] dp[i][j] 为dfs序中前 i i 个节点用 j j 种颜色染色有多少种方案。
对于第 i i 个结点进行染色时有两种选择:

  1. 选择前 i 1 i - 1 个结点已经用过的颜色。那么这个结点能染的颜色一定是它父亲所染的颜色。因为无论与前面的哪个结点染色相同,路径都会经过父亲结点。
    这样选择前 i 1 i - 1 个结点用了 j j 种颜色。前 i 1 i - 1 个结点的方案数为 d p [ i 1 ] [ j ] dp[i - 1][j] 每种方案只对应一种 d p [ i ] [ j ] dp[i][j]
  2. 选择没有用过的颜色。这样选择前 i 1 i - 1 个结点会用 j 1 j - 1 种颜色。前 i 1 i - 1 个结点的方案数为 d p [ i 1 ] [ j 1 ] dp[i - 1][j - 1] 。对于每个前i - 1的方案,第j种颜色的选择会有 k j + 1 k - j + 1 种,所以每个前i - 1的方案对应 k j + 1 k - j + 1 d p [ i ] [ j ] dp[i][j] 的方案。
    这样转移方程就可以写出来了。可以发现其实与给出的树形结构没有任何关系。

代码:

/**
* Author : Xiuchen
* Date : 2020-04-09-19.11.59
* Description : dfs序 + 动态规划
*/
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
const ll mod = 1e9 + 7;
const int maxn = 310;
int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}
int n, k;
ll dp[maxn][maxn];
vector<int> G[maxn];
int main(){
    scanf("%d%d", &n, &k);
    int x, y;
    for(int i = 1; i <= n - 1; i++){
        scanf("%d%d", &x, &y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    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) % mod) % mod;
    }
    ll ans = 0;
    for(int i = 1; i <= k; i++) ans = (ans + dp[n][i]) % mod;
    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44607936/article/details/105420105