牛客练习赛1 树 dp + dfs序

题目链接

题解:

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

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

  1. 选择前 i − 1 i - 1 i1个结点已经用过的颜色。那么这个结点能染的颜色一定是它父亲所染的颜色。因为无论与前面的哪个结点染色相同,路径都会经过父亲结点。
    这样选择前 i − 1 i - 1 i1个结点用了 j j j种颜色。前 i − 1 i - 1 i1个结点的方案数为 d p [ i − 1 ] [ j ] dp[i - 1][j] dp[i1][j]每种方案只对应一种 d p [ i ] [ j ] dp[i][j] dp[i][j]
  2. 选择没有用过的颜色。这样选择前 i − 1 i - 1 i1个结点会用 j − 1 j - 1 j1 种颜色。前 i − 1 i - 1 i1个结点的方案数为 d p [ i − 1 ] [ j − 1 ] dp[i - 1][j - 1] dp[i1][j1]。对于每个前i - 1的方案,第j种颜色的选择会有 k − j + 1 k - j + 1 kj+1种,所以每个前i - 1的方案对应 k − j + 1 k - j + 1 kj+1 d p [ i ] [ j ] dp[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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(牛客竞赛,动态规划,思维)