nowcoder NC - 13611 树(dp || 数论)

5 篇文章 0 订阅

题目链接:https://ac.nowcoder.com/acm/problem/13611

题意:shy有一颗树,树有n个结点。有k种不同颜色的染料给树染色。一个染色方案是合法的,当且仅当对于所有相同颜色的点对(x,y),x到y的路径上的所有点的颜色都要与x和y相同。请统计方案数。

思路:
1、 d p dp dp
我们用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示用 j j j种颜色去染 i i i个结点的方案数,那我就有两种情况:
(1)、 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ; dp[i][j] = dp[i-1][j]; dp[i][j]=dp[i1][j];
(2)、 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] × ( k − ( j − 1 ) ) ; dp[i][j] = dp[i-1][j-1]\times(k - (j-1)); dp[i][j]=dp[i1][j1]×(k(j1));
所以 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]\times(k - (j-1)); dp[i][j]=dp[i1][j]+dp[i1][j1]×(k(j1));
最后答案就是 ∑ i = 1 k d p [ n ] [ i ] \sum_{i=1}^{k}dp[n][i] i=1kdp[n][i]
就搞不懂这个题给我们一颗树干嘛

AC代码如下:

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define LL long long
#define pii pair<int,int>
#define sd(x) scanf("%d",&x)
#define slld(x) scanf("%lld",&x)
#define pd(x) printf("%d\n",x)
#define plld(x) printf("%lld\n",&x)
#define rep(i,a,b) for(int i = a ; i <= b ; i++)
#define per(i,a,b) for(int i = b ; i >= a ; i--)
#define mem(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define fast_io ios::sync_with_stdio(false)

const int INF = 1e9;
const LL mod = 1e9 + 7;
const int maxn = 3e2 + 7;

LL dp[maxn][maxn];

int main() {
    int n,k;
    while(~scanf("%d%d",&n,&k)) {
        for(int i = 1 ; i < n ; i++) {
            int u,v;
            scanf("%d%d",&u,&v);
        }
        memset(dp,0,sizeof(dp));
        dp[0][0] = 1;
        for(int i = 1 ; i <= n ; i++) {
            for(int j = 1 ; j <= min(i,k) ; j++) {
                dp[i][j] = (dp[i-1][j] + dp[i-1][j-1] * (1LL * (k - j + 1))) % mod;;
            }
        }
        LL ans = 0;
        for(int i = 1 ; i <= k ; i++) {
            ans = (ans + dp[n][i]) % mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

2、数论
首先我们分析一下这个题目,其实就是让我们将这棵树分成最多 m i n ( n , k ) min(n,k) min(n,k)个连通块,每个连通块里的结点颜色相同,求方案数。
既然如此,我们要将这棵树分成若干个连通块,那我们就要删边,而且,如果我们要将这棵树分成 i i i个连通块,那我们就需要删掉 i − 1 i-1 i1条边,那方案数就是
C ( n − 1 , i − 1 ) C(n-1,i-1) C(n1,i1),那我们现在就需要对已经分好的 i i i个连通块进行染色,一共 k k k种颜色,取 i i i个,而且还要考虑顺序,所以方案数就是 A ( k , i ) A(k,i) A(k,i),那么答案就是 ∑ i − 1 m i n ( n , k ) C ( n − 1 , i − 1 ) × A ( k , i ) \sum_{i-1}^{min(n,k)}C(n-1,i-1)\times A(k,i) i1min(n,k)C(n1,i1)×Ak,i

AC代码如下:

#include <set>
#include <map>
#include <stack>
#include <queue>
#include <cmath>
#include <ctime>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define LL long long
#define pii pair<int,int>
#define sd(x) scanf("%d",&x)
#define slld(x) scanf("%lld",&x)
#define pd(x) printf("%d\n",x)
#define plld(x) printf("%lld\n",x)
#define rep(i,a,b) for(int i = (a) ; i <= (b) ; i++)
#define per(i,a,b) for(int i = (b) ; i >= (a) ; i--)
#define mem(a) memset(a,0,sizeof(a))
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define fast_io ios::sync_with_stdio(false)

const int INF = 1e9;
const LL mod = 1e9 + 7;
const int maxn = 3e2 + 7;

LL f[maxn],inv[maxn];

LL C(LL x,LL y) {
    return f[x] * inv[y] % mod * inv[x-y] % mod;
}

LL A(LL x,LL y) {
    return f[x] * inv[x-y] % mod;
}

int main() {
    inv[0] = f[0] = inv[1] = f[1] = 1;
    for(LL i = 2 ; i <= maxn ; i++) {
        inv[i] = ((mod - mod / i) * inv[mod%i]) % mod;
        f[i] = i;
    }
    for(LL i = 2 ; i <= maxn ; i++) {
        inv[i] = (inv[i] * inv[i-1]) % mod;
        f[i] = (f[i] * f[i-1]) % mod;
    }
    LL n,k;
    while(~scanf("%lld%lld",&n,&k)) {
        rep(i,1,n-1) {
            LL x,y;
            scanf("%lld%lld",&x,&y);
        }
        LL ans = 0;
        for(LL i = 1 ; i <= n && i <= k ; i++) {
            ans = (ans + (C(n-1,i-1) * A(k,i) % mod)) % mod;
        }
        plld(ans);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值