Down Below

因为看到题解区全是二分的 nmlogV 做法, 感觉不太优秀,所以就写了这篇题解。

观察题目,很容易联想到最短路,走到某个点的 val 就相当于路径长度,而由于有 ai 的限制,所以我们要尽量让 val 大,也就相当于要求最长路径,而 bi 就相当于是路径长度。

先考虑转化问题,最优问题通过二分转化成可行性问题。接着按照刚才的思路,运用类似于最短路的方法去处理询问。这里涉及到一个选择算法的问题,但由于求的是最长路径,首先排除 dijkstra ,接着排除肯定超时的 Floyd ,那结果显而易见是只能是 SPFA 。具体为什么不是 Bellman–Ford 我们后面会说明。

但我们进行最短路的时候会发现,bi 只能算一次的性质让我们这个算法显得没那么正确了, 因为如果我们走了一个环, 那么我们就可以一直循环,再走出去。我们再回到题目,环其实是一个很特殊的东西,如果从 1 能走到一个环,那么我们可以把这个环及其到 1 的路径一起合并成一个新的 1 ,原因就在于本来我们不能走回头路,但是有了环以后我们就可以利用环掉头。又因为每个点的度都是大于 2 的,那么每个点就一定至少在一个环上或者在一条环到 1 的路径上, 否则一定存在度数为 1 的点。所以我们的目标就从求最长路变成了求正环,这个问题似乎正是 SPFA 擅长的。

当我们开始用 SPFA 去求解正环的时候却发现,并不是每个正环都是可行的,有时候我们会因为重复跑环而跑到一些本来到不了的点,从而合并到了本来到不了的环,并且 SPFA 跑环的时间也并不正确,每次时间复杂度 O(nk) ,最多跑 m 次。我们需要发现些性质才能解决这个问题。由于我们一条边可以走的条件是 val>ai ,那么我们考虑如果一个点在 SPFA 的过程中被更新了两次,那么设这个点为 u ,两次尝试更新它的点分别是 v,w (假设这个点是第一个被尝试更新两次的点,那么不可能从同一个人尝试更新两次, 否则更新它的那个点一定被更新过至少两次),假设 valwvalv ,那么有 valu=bu+valw>valwvalv>avvalu>av ,所以 u 可以从 w 走过来并且再从 v 走回点 1 , 所以 u 在一个环上, 那么我们直接记录从 1 走到 v,w 的路径即可, 由于这是第一个环,所以 v,w 都有唯一的从 1 来的路径。

因为我们每次合并至少会减少一个点,那么我们可以在每次合并后重新做 SPFA ,就可以了。这里解释一下刚才没有说的问题, 为什么要用 SPFA 而不用 Bellman–Ford 。可以发现在 SPFA 中一个点如果第二次进入队列那么就一定至少被尝试更新两次,那么就已经找到环了。这样来看的话 SPFA 的总时间应该是 m 的,而 Bellman–Ford 每次会用 m 的时间进行一个点的更新,那么对于类似于树的图的时候,他找到环的时间复杂度会退化成 nm

至此我们已经得到了一个 O(nmlogV) 的能过的算法,而且这个算法如果打的漂亮也可以很快,但是很明显我们的目标并不是这样的一个算法,因为我们可以发现似乎二分的过程并不是必须的,我们明明知道要扩展更多需要使答案增加多少,我们二分却完全没有用到这个性质。

考虑我们最开始假如 1val0 那么我们一定无法继续进行寻找最长路径和缩环,那么假设 u 是一个我们能到达的点,v 为一个 u 的邻点且满足 v 不能到达,则我们要能使更多的点被到达至少要将答案增加的值就是 min{avvalu} 。所以只要我们没能将所有点都合成一个点,我们就尝试缩环或者无法缩环的时候选择增加答案,然后再重新做 SPFA ,那么缩点只会缩 n 次,更新答案只会因为一条边更新一次,总共就只会最多更新 m 次。所以最终也是有了一个 O(m2) 的算法了,虽然因为每次更新其实增加的能扩展的边可以直接加入队列中,所以可以优化到 O(nm) ,但是因为懒就没有打了( O(m2) 已经可以将本来跑 200ms 多的快到最劣解的算法优化成 2023.10.19 之前的最优解我觉得已经足够了,而且再优化因为常数的原因可能时间并不会有太大差别)。

code:

#include <cstdio> #include <algorithm> #include <stack> #include <vector> using namespace std; #define MAXN 1000 #define MAXM 2000 #define INF 0x3f3f3f3f vector<pair<int, int> > s[MAXN + 5]; long long dp[MAXN + 5]; long long vis[MAXN + 5]; pair<int, int> sed[MAXM + 5]; int Fa[MAXN + 5]; long long sa[MAXN + 5], sb[MAXN + 5]; int main () { int t; scanf ("%d", &t); while (t --) { int n, m; scanf ("%d %d", &n, &m); for (int i = 2; i <= n; i ++) { scanf ("%lld", &sa[i]); } for (int i = 2; i <= n; i ++) { scanf ("%lld", &sb[i]); } for (int i = 1; i <= n; i ++) { s[i].clear(); } for (int i = 1; i <= m; i ++) { scanf ("%d %d", &sed[i].first, &sed[i].second); } long long ans = 0; int num = n - 1; for (int i = 1; i <= n; i ++) { vis[i] = 0; } while (num) { long long smin = INF; int Last = n; while (num && num != Last){ Last = num; for (int i = 1; i <= n; i ++) { dp[i] = 0; s[i].clear(); Fa[i] = 0; } dp[1] = ans; for (int i = 1; i <= m; i ++) { int u = sed[i].first, v = sed[i].second; if (vis[u]) { u = 1; } if (vis[v]) { v = 1; } if (u != v) { s[u].push_back (make_pair(v, i)); s[v].push_back (make_pair (u, i)); } } for (int i = 2; i <= n; i ++) { if (vis[i]) { dp[1] += sb[i]; } } stack<pair<int, int> > S; S.push(make_pair (1, 0)); vis[1] = 1; while (!S.empty()) { pair<int, int> p = S.top(); S.pop(); for (auto i = s[p.first].begin(); i != s[p.first].end(); i ++) { if (i->second == p.second) { continue; } else if (Fa[i->first] || i->first == 1) { for (int j = i->first; j != 1; j = Fa[j]) { if (!vis[j]) { vis[j] = 1; num --; } else { break; } } for (int j = p.first; j != 1; j = Fa[j]) { if (!vis[j]) { vis[j] = 1; num --; } else { break; } } } else if (dp[p.first] > sa[i->first]) { dp[i->first] = sb[i->first] + dp[p.first]; Fa[i->first] = p.first; S.push(*i); } else { smin = min (smin, sa[i->first] + 1 - dp[p.first]); } } } } if (num) { ans += smin; } } printf ("%lld\n", ans); } }

__EOF__

本文作者FlowerDream
本文链接https://www.cnblogs.com/flower-dream/p/17775855.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   小篪篪  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
Live2D
欢迎阅读『Down Below』
点击右上角即可分享
微信分享提示