1 条题解
-
0
自动搬运
来自洛谷,原作者为

dbxxx
多刷题,少整那些没用的搬运于
2025-08-24 22:42:47,当前版本为作者最后更新于2022-10-31 01:34:35,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
下文中,
- 和 之间可达意为 和 之间可以经过不多于 次转车到达,即 到 的距离不多于 。
- 一个点 在家附近,意为 和 之间可达。
注意,为方便,特殊地,我们认为自己和自己不可达。
观察数据范围, 可过,已经足够处理出每个点对之间的距离了。因此首先应该采用 bfs, 计算任意点对之间是否可达。
注意:对于满足边权全为 的图,单源最短路可以做到 的复杂度(采用 bfs);
对于满足边权只有 , 两种的图,单源最短路也可以做到 的复杂度(采用 0-1 bfs)。
我们考虑一条 的路径, 的枚举是不可行的。
很直接的想法是,依次考虑 ,,,,发现贪心是不对的,从 选择了更大的权值景点 ,但是可能接下来能到达的 就小的可怜;而选一个权值稍小一点的 ,可能可达的 会很大。
但有一种贪心是对的,那就是确定了 ,,,选择 的时候。如果我们确定了 ,那么直接选择 可达的,在家附近的,不为 也不为 的权值最大的景点作为 即可。反正 之后没有景点了,所以可以直接贪心,没有后顾之忧。
由于环的对称性,可以发现确定了 ,, 之后,也可以贪心地选择 。
有了大体思路。
我们定义 表示 可达,且在家附近的权值最大的景点。
第一步:我们预处理出 。
第二步:直接 枚举,循环确定景点 和 (),然后记 ,,然后试图用 更新答案,其中 表示景点 的权值。
发现重复性的细节是存在问题的,比如:会出现 的情况,此时让 会导致 ,这是不允许的。
不过,贪心思想还是不变的: 应该尝试更换为 可达,且在家附近的权值第二大的景点。
更换定义, 表示 可达,且在家附近的权值第 大的景点。
当发现 时,将 设置为 即可。
当发现 时,将 设置为 即可。
发现并没有完全解决:如果这样处理后的 和 仍然重复怎么办?
还是贪心思想,考虑把 或者 换成更小的那个比较一下就行,具体来说,比如原先 ,就把 下调为 ;或者原先 ,就把 下调为 ,然后比较两种方案哪种更好就行了。
重复性解决了,但是还有个细节:如果原先 ,发现 ,我们想下调 。是直接把 设置成 吗?错误,我们还需要检查一下 是否等于 ,如果等于 还需要继续下调到 。下调 同理。
至于原先 为啥下调 不需检查?因为如果原先 了说明 已经等于 了。所以 肯定什么问题都没有。
如果直接这么写是没有问题的,但是麻烦了。下面是一种更好写的处理方法:
直接分别枚举 分别作为 ,, 和 分别作为 ,, 组成的共九种情况,检查互异性然后更新答案即可。
需要提前预处理出 ,其实只要在最开始 bfs 预处理的同时维护一下就行了。
注意某些点可达,还在家附近的点数可能没有 个,因此注意类似访问 时产生的越界问题。
如果枚举到某个 ,,发现 或者 有对应点数不超过 的情况时,这组 , 不一定可以生成一组合法的解,属于正常现象。
题目保证全局上是有解的。
/* * @Author: crab-in-the-northeast * @Date: 2022-10-30 17:40:03 * @Last Modified by: crab-in-the-northeast * @Last Modified time: 2022-10-30 19:07:52 */ #include <bits/stdc++.h> #define int long long inline int read() { int x = 0; bool flag = true; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') flag = false; ch = getchar(); } while (isdigit(ch)) { x = (x << 1) + (x << 3) + ch - '0'; ch = getchar(); } if(flag) return x; return ~(x - 1); } const int maxn = 2505; typedef std :: pair <int, int> pii; std :: vector <int> G[maxn]; int w[maxn]; bool ok[maxn][maxn]; // u, v 是否可达 std :: vector <int> f[maxn]; // f[u] 存放:可达 u 且可达 1 的前三大 v int k; int dis[maxn]; void bfs(int x) { std :: memset(dis, -1, sizeof(dis)); std :: queue <int> q; q.push(x); dis[x] = 0; while (!q.empty()) { int u = q.front(); q.pop(); if (u != x) { ok[x][u] = true; if (x != 1 && ok[1][u]) { // printf("%lld %lld\n", x, u); f[x].push_back(u); std :: sort(f[x].begin(), f[x].end(), [](int u, int v) { return w[u] > w[v]; }); // 注意这里 sort 元素数量不超过 3,效率可看做常数 if (f[x].size() > 3) f[x].pop_back(); } } if (dis[u] == k + 1) continue; for (int v : G[u]) if (dis[v] == -1) { dis[v] = dis[u] + 1; q.push(v); } } } inline bool gmx(int &a, int b) { return b > a ? a = b, true : false; } signed main() { int n = read(), m = read(); k = read(); for (int u = 2; u <= n; ++u) w[u] = read(); while (m--) { int u = read(), v = read(); G[u].push_back(v); G[v].push_back(u); } for (int u = 1; u <= n; ++u) bfs(u); int ans = 0; for (int b = 2; b <= n; ++b) for (int c = 2; c <= n; ++c) if (ok[b][c]) for (int a : f[b]) for (int d : f[c]) if (a != c && b != d && a != d) // 其他不等关系天然满足了,只有这三组需要检验; // 天然满足的不等关系:a != b,b != c,c != d,请读者自己思考为什么。 gmx(ans, w[a] + w[b] + w[c] + w[d]); printf("%lld\n", ans); return 0; }如果觉得这篇题解写得好,请不要忘记点赞,谢谢!
- 1
信息
- ID
- 7873
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者