uestc summer training #9 牛客第三场 BFS计数
G.coloring tree BFS计数
题目:给你n(<=5000)个节点的一颗树 你有K(<=5000)种颜色 你可以给每一个节点染一种颜色
总共有Kn种染色方法 在一种染色方法中 定义colorness为每一对相同颜色节点距离中的最短距离
问colorness为D时 有多少种染色方案 结果%mod
解:我们先求出>=D的方案数再减去>=D+1的方案数就是==D的方案数
现在问题变为>=X的方案数 可以看成对于任意一个节点,所有和它距离小于d的节点,不能和它有相同的颜色
然后我们随便找个根节点开始bfs染色,怎么染色呢?
我们考虑根节点,它有k种颜色可以染,我们给根染了一种颜色之后,所有和根在距离<d的点,可以被染的颜色都会少一种,
然后我们bfs对所有的节点都这样做,然后我们最后把每个点可以染的颜色种类数乘起来,就是我们需要的答案了。
上述思路代码实现应该是很简单的,因为数据范围只有5000,我们可以对每个点都dfs一次,求出所有点之间的距离。然后计算的时候每次都把距离<d的所有点可选颜色都-1。
代码实现不是主要部分,重点是为什么按照bfs的顺序去做可以保证答案正确呢?
这里就要考虑一个问题了。假如我们有两种颜色,每个节点不能和距离<=1的点有相同的颜色,我们要怎么做呢?
比如我们有三个节点
1->2->3
如果我们不按照bfs顺序来染色
1有两种染色方案,3也有两种染色方案,这样一看,2就没有染色方案了,最后乘出来的答案就会为0。这是为什么呢?
我们发现1和3可以选取的颜色有重复,然后作用到2这个节点就会出问题。
那么 为什么bfs能保证答案正确呢?
我画了很久的图。。发现了一个性质。。
如果我们按照bfs的顺序来选择颜色计算方案数的话,已经选过颜色的和当前节点距离在d以内的节点,一定两两距离都在d以内
就是说,当前节点距离d以内的已经选择过颜色的节点在选择颜色种类的时候一定会互相考虑到彼此节点,如果我们用dfs,就保证不了这个性质了。
感谢牛客多校群的大佬帮我完成了这个证明
当前bfs到的节点 和其它(bfs走过的&&到当前节点距离<d)的点,两两之间距离<d的证明如下:
dis(u,v)=dis(u,x)+dis(v,x)-2*dis(lca(u,v),x) 当前枚举点为x,u,v是任意点,lca是对于x为根 设u为深度比较低的那个点, dis(u,x)-dis(lca(u,v),x)是大于等于dis(v,lca(u,v))的 然后代进去就能发现dis(u,v)<=dis(v,x)的
/*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; typedef unsigned long long ull; const double eps = 1e-9; const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}}; const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9; const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, MAXQ = 100010, INF = 1e9; const ll LLINF = (1LL << 50); int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1; inline void addedge(int u, int v) { if (u == v) { return ; } to[++tot] = v; nxt[tot] = Head[u]; Head[u] = tot; } template <typename T> inline void read(T&x) { char cu = getchar(); x = 0; bool fla = 0; while (!isdigit(cu)) { if (cu == '-') { fla = 1; } cu = getchar(); } while (isdigit(cu)) { x = x * 10 + cu - '0', cu = getchar(); } if (fla) { x = -x; } } ll n, k, d; ll dis[5005][5005]; ll val[5005]; void dfs(int s, int now, int pre) { for (int v, i = Head[now]; i; i = nxt[i]) { v = to[i]; if (v == pre) { continue; } dis[s][v] = dis[s][now] + 1; dfs(s, v, now); } } ll get_ans(ll d) { queue<ll> q; q.push(1); for (int i = 1; i <= n; i++) { val[i] = k; } ll ans = 1; while (!q.empty()) { ll i = q.front(); q.pop(); ll res = 0; for (int j = 1; j <= n; j++) { if (i != j) { val[j] -= dis[i][j] < d; } } if(val[i]<=0) return 0; ans=ans*val[i]%mod; for(int v,j=Head[i];j;j=nxt[j]) { v=to[j]; if(dis[1][i]+1==dis[1][v]) { q.push(v); } } } return ans; } int main() { ios_base::sync_with_stdio(0); cin.tie(0); read(n), read(k), read(d); int u, v; for (int i = 1; i < n; i++) { read(u), read(v); addedge(u, v), addedge(v, u); } for (int i = 1; i <= n; i++) { dfs(i, i, -1); } ll anser = (get_ans(d) - get_ans(d + 1) + mod) % mod; printf("%lld\n", anser); return 0; }