树(4月7日题目 dfs序 数论 排列组合)

https://ac.nowcoder.com/acm/problem/13611
https://ac.nowcoder.com/discuss/401225

题目描述
shy有一颗树,树有 n n n 个结点。有 k k k 种不同颜色的染料给树染色。一个染色方案是合法的,当且仅当对于所有相同颜色的点对 ( x , y ) (x,y) (x,y) x x x y y y 的路径上的所有点的颜色都要与 x x x y y y 相同。请统计方案数。
输入描述:
第一行两个整数 n n n k k k 代表点数和颜色数;
接下来 n − 1 n-1 n1 行,每行两个整数 x , y x,y x,y 表示 x x x y y y 之间存在一条边;
输出描述:
输出一个整数表示方案数 ( m o d   1 e 9 + 7 ) (mod \ 1e9+7) (mod 1e9+7)
示例1
输入
4 3
1 2
2 3
2 4
输出
39
备注:
对于 30 % 30\% 30% 的数据, n ≤ 10 , k ≤ 3 n≤10, k≤3 n10,k3
对于 100 % 100\% 100% 的数据, n , k ≤ 300 n,k≤300 n,k300

思路:
这道题可以转化成一道排列组合的问题,将这棵树分成不超过 k k k 个连通块,每个连通块图涂不同的颜色。若将一棵树分成 i i i 个连通块,那么要删去 i − 1 i-1 i1 条边,方案数为 C n − 1 i − 1 \mathrm{C}_{n-1}^{i-1} Cn1i1,然后从 k k k 种颜色中选 i i i 种染色,方案数为 A k i \mathrm{A}_{k}^{i} Aki
所以,总的方案数就是 ∑ i = 1 m i n ( n , k ) C n − 1 i − 1 A k i \sum_{i = 1}^{min(n,k)}\mathrm{C}_{n-1}^{i-1}\mathrm{A}_{k}^{i} i=1min(n,k)Cn1i1Aki

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 310;
ll n, k, ans, fac[N], inv[N];
ll C(ll a, ll b) {
    return fac[a] * inv[b] % mod * inv[a - b] % mod;
}
ll A(ll a, ll b) {
    return fac[a] * inv[a - b] % mod;
}
int main() {
    ios::sync_with_stdio(false);
    cin >> n >> k;
    inv[0] = inv[1] = fac[0] = fac[1] = 1;
    for (ll i = 2; i <= max(n, k); i++) {
        inv[i] = (mod - mod / i) * inv[mod % i] % mod; //公式递推求 i 的逆元
    }
    for (ll i = 2; i <= max(n, k); i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = inv[i] * inv[i - 1] % mod;
    }
    for (ll i = 1; i <= min(n, k); i++) {
        ans = (ans + C(n - 1, i - 1) * A(k, i) % mod) % mod;
    }
    cout << ans << endl;
    return 0;
}

Code2:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const int N = 310;
ll n, k, ans, fac[N];
ll qpow(ll a, ll b) {
    ll res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res % mod;
}
ll Inv(ll a, ll p) {
    return qpow(a, p - 2);
}
ll C(ll a, ll b) {
    return fac[a] * Inv(fac[b], mod) % mod * Inv(fac[a - b], mod) % mod;
}
ll A(ll a, ll b) {
    return fac[a] * Inv(fac[a - b], mod) % mod;
}
int main() {
    ios::sync_with_stdio(false);
    cin >> n >> k;
    fac[0] = 1;
    for (ll i = 1; i <= max(n, k); i++) {
        fac[i] = fac[i - 1] * i % mod;
    }
    for (ll i = 1; i <= min(n, k); i++) {
        ans = (ans + C(n - 1, i - 1) * A(k, i) % mod) % mod;
    }
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值