首先可以知道如果一个点在最后答案的路径里, 对其进行成长后, 多的那些点也全部会在答案的路径上的. 我们可以通过总是选择断开原来图中就有的边, 而让路径在新生长的这个圈中绕一圈来做到. 也就是说, 如果一个点在答案的路径上, 那么它成长 M 次后的总点数就是他的贡献.
考虑如何快速算出成⻓后的点数, 可以发现, 在进行一次成⻓后的树度数最大为3, 对于一个 2 度点, 成⻓一次变为 2 个 2 度点, 对于一个 3 度点, 成⻓一次边为 2 个 2 度点和 1 个 3 度点,对于一个 d 度点, 成⻓一次为 2 个 2 度点和 d − 2 d-2 d−2 个 3 度点. 最后推出一个度数为 x 的点成长 m 次后会变成 ( x − 1 ) ∗ ( 2 m − 1 ) + 1 (x-1)*(2^m-1)+1 (x−1)∗(2m−1)+1个点.(补题时总是不想计算这些推理的, 所有我没推这玩意 )
接下来就考虑用这个式子作为每个点的权值, 来计算树中的最长路( 权值和最大的路径 ).
- 当 m 很小时, 运算结果不会超过 1 e 9 + 7 1e9+7 1e9+7, 我们可以直接计算出每个点的贡献来找最长路, 代码用的是找树的直径的 DP 算法, 也可以两遍 dfs 做, 详细讲解看 io wiki.
int Max = 0;//要的答案
vector<int>ans(n + 1, 0); // 子树中从子树的根到某个叶子的最长路
auto dfs = [&](auto self, int x, int fa) ->void {
for (auto c : eg[x]) {
if (c == fa)continue;
self(self, c, x);
Max = max(Max, ans[x] + ans[c] + (du[x] - 1) * (ksm(2, m) - 1) + 1);
ans[x] = max(ans[x], ans[c]);
}
ans[x] += (du[x] - 1) * (ksm(2, m) - 1) + 1;
};
dfs(dfs, 1, 1);
return Max;
- 当 m 很大时, 由于取模, 求得的结果就不具备可比大小的性质了.
但是观察 w = ( x − 1 ) ∗ ( 2 m − 1 ) + 1 w=(x-1)*(2^m-1)+1 w=(x−1)∗(2m−1)+1这个式子, 会发现: 当 m > 20 m>20 m>20 时, 2 m 2^m 2m为 1e6 级别, 而后面的 1 在对所有点的贡献提取公因式后最大不会超过点的个数 1e5.
也就是说: 度数 x 每增大 1, 后面的常数变再大也影响不到最终答案是不是 Max. 就可以用 ( 2 m − 1 ) (2^m-1) (2m−1)的系数 => (路径的度数和减去路径的点数) 为第一关键字, 点的个数(也就是公式中常数 1 的个数 ) 为第二关键字, 作为权值, 就最长路, 最后再算答案. 为什么 m 较小时不可以这样做呢? 因为 Max 的度数和比较小者大 1,但较小者可能由很多点组成, 也就是最后的常数非常大, 2 m − 1 < n 2^m-1<n 2m−1<n时, 不能保证结果不会被影响.
eg: 第一关键字的"减去路径的点数"不能拿掉, 因为它使得第一关键字的大小受点的个数影响, 而有相同度数和的路径可能具有不用的点个数.
全部代码:
#define int long long
const int M = 1e9 + 7;
int ksm(int a, int n) {
int ans = 1;
while (n) {
if (n % 2)ans = ans * a % M;
a = a * a % M;
n /= 2;
}
return ans;
}
int solve(int _) {
int n, m, v, u;
cin >> n >> m;
vector<int>du(n + 1, 0);
vector<vector<int>>eg(n + 1);
for (int i = 1; i < n; ++i) {
cin >> v >> u;
eg[u].emplace_back(v);
eg[v].emplace_back(u);
du[u]++;
du[v]++;
}
if (m < 20) {
int Max = 0; //一位度数-数量, 二维数量
vector<int>ans(n + 1, 0);
auto dfs = [&](auto self, int x, int fa) ->void {
for (auto c : eg[x]) {
if (c == fa)continue;
self(self, c, x);
Max = max(Max, ans[x] + ans[c] + (du[x] - 1) * (ksm(2, m) - 1) + 1);
ans[x] = max(ans[x], ans[c]);
}
ans[x] += (du[x] - 1) * (ksm(2, m) - 1) + 1;
};
dfs(dfs, 1, 1);
return Max;
}
else {
pair<int, int>Max;//一位度数-数量, 二维数量
vector<pair<int, int>>dep(n + 1, {0, 0});
auto dfs = [&](auto self, int x, int fa) -> pair<int, int> {
for (auto c : eg[x]) {
if (c == fa)continue;
auto tp = self(self, c, x);
Max = max(Max, {dep[x].first + tp.first + du[x] - 1, dep[x].second + 1 + tp.second});
dep[x] = max(dep[x], tp);
}
dep[x].first += du[x] - 1;
dep[x].second++;
return dep[x];
};
dfs(dfs, 1, 1);
return (Max.first * (ksm(2, m) - 1 + M) % M + Max.second) % M;
}
}
signed main()
{
// freopen("input.txt", "r", stdin);
ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
int _ = 1;
cin >> _;
while (_--) {
cout << solve(_) << endl;
}
}