LCA->火车运输(洛谷1967)
1.双向广搜-> hdu11952.双向广搜->字符变换(洛谷P1032)3.双向广搜->奶牛集合(洛谷p3067)4.双向广搜->世界冰球锦标赛(洛谷p4799)5.线段树->(Acwing 4344)6.线段树->线段树2(洛谷3373)7.树上简单问题->树的重心8.简单树上问题->树的直径9.树上简单问题->巡逻(洛谷3629)10.LCA->最近公共祖先(洛谷3379)11.LCA-> Max Flow P(洛谷3128)
12.LCA->火车运输(洛谷1967)
13.数位统计dp->烦人的数学作业(洛谷4999)14.数位DP统计->数字计数(洛谷2602)15.数位DP统计->Windy数(洛谷2657)16.数位DP统计->手机号码(洛谷4124)17.状态压缩DP-> 最短Hamilton路径(Acwing)18.状态压缩DP->蒙德里安的梦想(Acwing)19.状态压缩DP->炮兵阵地(Acwing)20.状态压缩DP->吃奶酪(洛谷1433)21.区间dp->石子合并(洛谷1775)22.区间dp->StringPainter(uva 1437)23.树形DP->苹果树(洛谷P2015)24.树形DP->没有上司的舞会(洛谷1352)题目链接:https://www.luogu.com.cn/problem/P1967
题意:n个点,m条边的图。q个询问,问u->v之间权值最小的边权值是多少(不要求走最短路径,只要经过路径上经过的最小的边的权值最大)。
分析:如果两点间有路径,那么优先走边权更大的边,所以先利用最大生成树算法构建树。在构建了树之后,构建lca。对于每个点对u, v,找出他们的lca,并记录每个点到lca的路径上权值最小的边权值。
如果是森林,在dfs入口的地方写错了,起点应该是i。
不一定要用vis数组判定是否访问过,也可以用depth == -1。
大顶堆是less, <号。小顶堆是>, greater, >号。
void solve(){
int n, m;
cin >> n >> m;
priority_queue<Edge, vector<Edge>, less<Edge>> pq;
for (int i = 1; i <= m; ++i){
Edge t;
cin >> t;
pq.emplace(t);
}
DisjointSet dsu(n + 1);
vector<vector<pair<int, int>>> al(n + 1);
while (!pq.empty()){
auto[u, v, w] = pq.top();
pq.pop();
if (dsu.isSameSet(u, v)){
continue;
}
dsu.unionSet(u, v);
al[u].emplace_back(v, w);
al[v].emplace_back(u, w);
}
int p = upper_bound(pow2_values.begin(), pow2_values.end(), n) - pow2_values.begin() - 1;
vector<vector<int>> dp(n + 1, vector<int> (p + 1));
vector<int> depth(n + 1, -1);
vector<vector<int>> weight(n + 1, vector<int> (p + 1, 0x3f3f3f3f));
vector<int> vis(n + 1);
function<void(int, int, int)> dfs = [&](int u, int p, int d){
depth[u] = d;
dp[u][0] = p;
vis[u] = 1;
for (int i = 1; (1 << i) <= d; ++i){
dp[u][i] = dp[dp[u][i - 1]][i - 1];
weight[u][i] = min(weight[u][i - 1], weight[dp[u][i - 1]][i - 1]);
}
for (const auto& [v, w] : al[u]){
if (v != p && vis[v] == false){
weight[v][0] = w;
dfs(v, u, d + 1);
}
}
};
for (int i = 1; i <= n; ++i){
if (!vis[i]){
dfs(i, 0, 1);
}
}
auto cal = [&](int u, int v) -> int{
int res = int(2e9);
if (depth[u] < depth[v]){
swap(u, v);
}
for (int i = p; i >= 0; --i){
if (depth[u] - (1 << i) >= depth[v]){
res = min(res, weight[u][i]);
u = dp[u][i];
}
}
if (u == v){
return res;
}
for (int i = p; i >= 0; --i){
if (dp[u][i] != dp[v][i]){
res = min({res, weight[u][i], weight[v][i]});
u = dp[u][i];
v = dp[v][i];
}
}
//!注意,此时的u和v是祖先下面一层的节点
res = min({res, weight[u][0], weight[v][0]});
return res;
};
int q;
cin >> q;
while (q --){
int u, v;
cin >> u >> v;
if (dsu.isSameSet(u, v) == false){
cout << -1 << '\n';
continue;
}
cout << cal(u, v) << '\n';
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通