CF771F : Bear and Isomorphic Points
题目传送门
题意:
在一棵树上,每一步最多走 k 步,问走完所有点对最少需要多少步
思路 && 分析:
设想一个这样的情况,树上任意两点之间的距离都是 k 的倍数,在这种情况下答案就是将所有的点对距离之和加起来(这个只需要一个简单的的 dfs 就能求出)在除以 k,但是这种情况显然只有在 k 为 1 时才会出现。
所以如果我们可以求出两个点之间的距离需要补多少就能被 k 整除(意思就是假如有两个点之间距离为 7, k 为 3,这种情况就要补 2。)在加上所有点的距离之和后除以 k 就会得到我们要的答案
具体见代码如下
#include<iostream>
#include<vector>
#include<algorithm>using namespace std;
typedef long long ll;
const int N = 200010;
vector<int> e[N];
// dp[i][j] 表示 i 的子树(下面写为 i 树)内有多少到根节点距离取余 m 为 j 的点
ll ans, dp[N][6];
// h 用来存每一个节点包括子树和自己有多少个点
int n, h[N], m;
void dfs(int u, int fa, int d){h[u] = 1;dp[u][d % m] = 1;for(auto s : e[u]){if(s == fa) continue;dfs(s, u, d + 1);//枚举组合 u 树内的节点与 s 内的节点for(int i = 0;i < m;i ++)for(int j = 0;j < m;j ++){//(i - d) + (j - d) --- (因为dp里的距离是到根节点的)// 这里 x 是两点之间的距离, y 即是要补的int x = (i + j - 2 * d) % m;int y = (m - x) % m;ans += 1LL * y * dp[u][i] * dp[s][j];}h[u] += h[s];for(int i = 0;i < m;i ++) dp[u][i] += dp[s][i];// 这里是原来的距离之和ans += 1LL * h[s] * (n - h[s]);}}
int main(){ios::sync_with_stdio(false);cin.tie(nullptr);cin >> n >> m;for(int i = 1;i < n;i ++){int x, y;cin >> x >> y;e[x].push_back(y);e[y].push_back(x);}dfs(1, 0, 0);cout << ans / m;return 0;
}