dp做法

时间复杂度

在树中选择任意一个叶子结点作为起点,沿着边一个点接一个点的涂色。定义为前个点使用了种颜色的方案数,状态转移方程:

  • 若第个点选用未使用的颜色,则
  • 若第个点选用已经使用过的颜色,则必须和的颜色相同,若和更前面的结点颜色相同则在路径中会经过不符合题意,则
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;     ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf;   ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 5e5 + 117;
const int MAXM = 2e5 + 117;

int n, k;
int u, v;
LL dp[377][377];
int main() {
    scanf("%d%d", &n, &k);
    for(int i = 1; i < n; i++) scanf("%d%d", &u, &v);
    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;
        }
    }
    for(int j = 1; j <= k; j++) {
        dp[n][j] = (dp[n][j] + dp[n][j - 1]) % mod;
    }
    printf("%lld\n", dp[n][k]);
    return 0;
}

连通块做法

时间复杂度,下面代码中的逆元可以进一步优化。

问题转化为把一棵树分成多个连通块,每个连通块内的结点涂相同的颜色,不同的连通块颜色不同,有多少种方案。

  • 若把个结点的树分成个连通块,则需要删除条边,有种方案。
  • 种颜色需要选出种,有种方案。
  • 种颜色涂个连通块,有种方案。
  • 上面三个条件满足乘法原理,则对答案的贡献为,最后只需要枚举一下可以分成多少个连通块求和即可。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int INF = 0x3f3f3f3f;     ///1 061 109 567
const int negative_infinite = 0xcfcfcfcf;   ///-808 464 433
const double pi = acos(-1);
const int mod = 1e9 + 7;
const double eps = 1e-8;
const int MAXN = 5e5 + 117;
const int MAXM = 2e5 + 117;

int n, k;
int u, v;
LL fac[377], sum;
LL fast_pow(LL a, LL n) {
    LL ret = 1, temp = a % mod;
    while(n) {
        if(n & 1) ret = ret * temp % mod;
        temp = temp * temp % mod;
        n >>= 1;
    }
    return ret;
}
LL Comb(int n, int m) {
    return fac[n] * fast_pow(fac[m] * fac[n - m], mod - 2) % mod;
}
int main() {
    scanf("%d%d", &n, &k);
    for(int i = 1; i < n; i++) scanf("%d%d", &u, &v);
    fac[0] = fac[1] = 1;
    for(int i = 2; i < 377; i++) fac[i] = fac[i - 1] * i % mod;
    if(k > n) k = n;
    for(int i = 1; i <= k; i++) {
        sum = (sum + Comb(n - 1, i - 1) * Comb(k, i) % mod * fac[i]) % mod;
    }
    printf("%lld\n", sum);
    return 0;
}

dfs序

总感觉以前听学长提起过,自己最近好像用过,遂翻了翻笔记,原来打Codeforces Round #629 (Div. 3)的E题时用到了,笔记地址http://www.kindkidll.com/index.php/archives/120/
下图是一颗树加了时间戳的遍历结果,dfs序的好处在于把树形结构转化成了线性结构。假设要进行操作把某棵子树的结点加上某个值,求某棵子树结点的和,转化成线性结构后问题就变成线段树的区间更新和区间查询了。
dfs