题目大意:一棵节点数为n的树,每条边有一个长度l和一个花费d,求一条路径,使得路径的总花费小于给定的m,且总长度最大
还是点分治,将这个无根树转为有根树之后,一条路径要么完全在某一子树下,要么经过根,子树的问题可以递归解决,
现在看经过根的情况,按序处理每一棵子树v,通过dfs可以得到该子树v所有节点到根的路径长度和花费,然后需要得到子树v这个集合
每一个点和已处理过子树的集合u内每一个点两两组合以更新答案,观察到如果集合v中路径a的花费高于b,但是a的
长度却小于b的时候,a一定不会在最优解中,因此可以将a删除,删除之后按花费排序后的长度也是有序的,因此可以枚举u中每一个
节点,去v中二分花费,然后更新最优长度,这样每层的复杂度还是nlogn,按重心点分治得到的是logn层,所以总的复杂度是nlog^2n
#include <cstdio>
#include <vector>
#include <utility>
#include <algorithm>
#include <cstring>
using namespace std;
struct node {
int v, d, l;
}; //保存树中的节点
const int INF = 0x3f3f3f3f;
const int maxn = 3e4 + 10;
int n, m, t, root, allnode, ans;
int maxson[maxn], sz[maxn], vis[maxn], depth[maxn], cost[maxn];
//maxson[i]: 去除节点i后得到的森林中节点数最多的树的节点
//sz:保存子树的节点数
//depth记录长度,cost记录花费
vector<pair<int, int> > left, right; //left为处理过的子树中点的集合,right为当前处理的子树的点的集合
vector<node> G[maxn]; //建图
//找重心
void find_root(int u, int fa) {
sz[u] = 1;
maxson[u] = 0;
for (int i = 0; i < G[u].size(); i++) {
node x = G[u][i];
int v = x.v;
if (v == fa || vis[v]) continue;
find_root(v, u);
sz[u] += sz[v];
maxson[u] = max(maxson[u], sz[v]);
}
maxson[u] = max(maxson[u], allnode - sz[u]);
if (maxson[root] > maxson[u]) root = u;
}
//计算子树所有节点的长度和花费
void dfs(int u, int fa) {
sz[u] = 1;
right.push_back({cost[u], depth[u]});
for (int i = 0; i < G[u].size(); i++) {
node x = G[u][i];
int v = x.v;
int d = x.d;
int l = x.l;
if (v == fa ||vis[v]) continue;
depth[v] = depth[u] + l;
cost[v] = cost[u] + d;
dfs(v, u);
sz[u] += sz[v];
}
}
void calc(int u) {
left.clear();
left.push_back({0, 0});
sz[u] = 1;
for (int i = 0; i < G[u].size(); i++) {
node x = G[u][i];
int v = x.v;
if (vis[v]) continue;
right.clear();
depth[v] = x.l;
cost[v] = x.d;
dfs(v, u);
sz[u] += sz[v];
sort(right.begin(), right.end());
//这里没有显示删除,而是将需要删除的节点的路径和长度赋值为和上一个节点
for (int j = 1; j < right.size(); j++) {
if (right[j].second <= right[j - 1].second) {
right[j].second = right[j - 1].second;
right[j].first = right[j - 1].first;
}
}
for (int j = 0; j < left.size(); j++) {
if (left[j].first > m) continue;
int pos = upper_bound(right.begin(), right.end(), pair<int, int>(m - left[j].first, INF)) - right.begin() - 1;
if (pos < 0) continue;
if (right[pos].second + left[j].second > ans) ans = right[pos].second + left[j].second;
}
for (int j = 0; j < right.size(); j++) {
left.push_back(right[j]);
}
}
}
void solve(int u) {
left.clear();
right.clear();
calc(u);
vis[u] = 1;
for (int i = 0; i < G[u].size(); i++) {
node x = G[u][i];
int v = x.v;
if (vis[v]) continue;
allnode = sz[v];
root = 0;
find_root(v, u);
solve(root);
}
}
int main() {
scanf("%d", &t);
int cas = 0;
while (t--) {
cas ++;
scanf("%d%d", &n, &m);
left.reserve(n);
right.reserve(n);
memset(vis, 0, sizeof(vis));
maxson[0] = n;
for (int i = 1; i <= n; i++) G[i].clear();
int u, v, d, l;
for (int i = 1; i < n; i++) {
scanf("%d%d%d%d", &u, &v, &d, &l);
G[u].push_back({v, d, l});
G[v].push_back({u, d, l});
}
root = ans = 0;
allnode = n;
find_root(1, -1);
solve(root);
printf("Case %d: %d\n", cas, ans);
}
return 0;
}