牛客网暑期ACM多校第三场G - 数论

题目链接:点击这里

解题思路:

处理这个问题之前我们先来考虑更简单的问题:

在长度为n的连续块上有m种染色颜料,而且距离小于等于k的块颜色不能相同的染色数是多少?

首先我们知道如果m<k,那么肯定无解。

那么染色方案数为:m*(m-1)*(m-2)*(m-3)*...*(m-k+1)*(m-k+1)*(m-k+1)....

到第k块时它就会受到前面k-1块的限制,必须两两不同,所以他的可选择方案数就是(m-k+1)

那么回到现在的问题其实就是在树上解决这个问题,最后的答案就是距离大于等于d的方案数-距离大于等于d+1的方案数就是等于d的方案数了。

解决的关键就在于怎么找到两两受限的地方,那么我们通过广度搜索序(bfs)搜索就可以得到两两受限的数量,然后就可以得到该节点的可选方案数。

#include<bits/stdc++.h>
using namespace std;
const int mx = 5e3 + 10;
const int mod = 1e9 + 7;
typedef long long ll;
int n,K,D,cnt[mx],top;
vector <int> vec[mx];
bool vis[mx];
void Order(int x,int d)
{
	queue <int> que;
	que.push(1);
	vis[1] = 1;
	while(!que.empty()){
		int no = que.front();
		que.pop();
		cnt[top++] = no;
		for(int v : vec[no]){
			if(vis[v]) continue;
			vis[v] = 1;
			que.push(v);
		}
	}
}
int dfs(int x,int fa,int d)
{
	if(d==D||!vis[x]) return 0;
	int ret = 1;
	for(int v : vec[x]){
		if(v==fa) continue;
		ret += dfs(v,x,d+1);
	}
	return ret;
}
ll solve()
{
	memset(vis,0,sizeof(vis));
	ll ans = 1;
	for(int i=0;i<n;i++){
		int v = cnt[i];
		vis[v] = 1; 
		int k = K - dfs(v,0,0) + 1;
		if(k<=0) return 0;//没有可选数
		ans = ans*k%mod;
	}
	return ans;
}
int main()
{
	scanf("%d%d%d",&n,&K,&D);
	int a,b;
	for(int i=1;i<n;i++){
		scanf("%d%d",&a,&b);
		vec[a].push_back(b);
		vec[b].push_back(a); 
	}
	Order(1,0);
	ll ans1 = solve();
	D++;
	ll ans2 = solve();
	printf("%lld\n",(ans1-ans2+mod)%mod);
    return 0;
}
扫描二维码关注公众号,回复: 2584625 查看本文章

猜你喜欢

转载自blog.csdn.net/a1214034447/article/details/81416168