当前位置: 首页 > news >正文

图论2026Mar

我的图论水平差到了一定地步,整理一些题目。
11th Mar 2026.

最短路

floyd

时间复杂度: \(O(n^3)\)

for (int k = 1; k <= n; ++k)for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j)if (d[i][j] > d[i][k] + d[k][j])d[i][j] = d[i][k] + d[k][j];

dijkstra

时间复杂度: \(O(n^2)\)

inline void djs (int cur) {for (int i = 1; i <= n; ++i) dis[i] = INF;dis[cur] = 0;for (int i = 1; i <= n; ++i) {int k = 0;for (int j = 1; j <= n; ++j) if (!vis[j]) {if (!k) k = j;else if (dis[k] > dis[j] && dis[j] != INF) k = j;}vis[k] = 1;for (auto &j: g[k]) dis[j.y] = std:: min(dis[j.y], dis[k] + j.z);}
}

堆优化的 dijkstra

时间复杂度: \(O((n+m)\log n)\) (所以有时候并不优)

inline void djs (int cur) {std:: priority_queue < NODE > pq;nd.x = cur, nd.z = 0; pq.push(nd);for (int i = 1; i <= n; ++i) dis[i] = INF;dis[cur] = 0;while (!pq.empty()) {nd = pq.top(); pq.pop();int i = nd.x;if (vis[i]) continue;vis[i] = true;for (auto &j: g[i])if (dis[i] + j.z < dis[j.to]) {dis[j.to] = dis[i] + j.z;nd.x = j.to, nd.z = dis[j.to]; pq.push(nd);}}
}

遇到负环:可以同时记录最短路的边数,边数 \(\geq n\) 则存在负环。

联通问题

  • 点双连通分量: 删一个点后 其余点仍然联通
  • 边双连通分量: 删一个边后 其余点仍然联通
  • 割点: 删去此点后不再联通
    当且仅当:点 \(u\) 存在儿子 \(v\) 使得 \(low_v \geq dfn_u\) 或点 \(u\) 为搜索的起始点且有 \(\geq 2\) 个儿子时 \(u\) 为割点。
  • 割边(桥): 删去此边后不再联通
    当且仅当 \(low_v > dfn_u\) 时边 \((u,v)\) 为割边。
    注意:有重边的时候,可以为边加上编号,同时限制无向边不走回头路即可。

以下各题均使用 tarjan 算法,利用时间戳跑 dfs 求解。时间复杂度 \(O(n+m)\)

P8435 【模板】点双连通分量

int n, m, ans;
std:: vector < int > g[500003], av[500003]; 
int dfn[500003], low[500003], cnt = 0;
std:: stack < int > s;
void dfs (int x) {dfn[x] = low[x] = ++cnt; s.push(x);for (auto &i: g[x]) if (!dfn[i]) {dfs(i);low[x] = std:: min(low[x], low[i]);if (low[i] >= dfn[x]) {++ans;while (s.size()) {int t = s.top(); s.pop();av[ans].push_back(t);if (t == i) break;}av[ans].push_back(x);}}elselow[x] = std:: min(low[x], dfn[i]);
}inline void kagari () {scanf("%d %d", &n, &m);for (int i = 1; i <= m; ++i) {int x, y; scanf("%d %d", &x, &y);if (x == y) continue;g[x].push_back(y), g[y].push_back(x);}for (int i = 1; i <= n; ++i) if (!dfn[i]) {if (g[i].size()) dfs(i);else av[++ans].push_back(i);}printf("%d\n", ans);for (int i = 1; i <= ans; ++i) {printf("%d ", av[i].size());for (auto &j: av[i]) printf("%d ", j);puts("");} return;
}

P3388 【模板】割点(割顶)

int n, m;
int head[20003], last[200003], to[200003], ccnt = 0;
#define addedge(x, y) last[++ccnt] = head[x], to[ccnt] = y, head[x] = ccnt
int dfn[20003], low[20003], cnt = 0;
bool f[20003]; int ans = 0;inline void dfs (int x, int fa) {dfn[x] = low[x] = ++cnt;int child = 0;#define y to[k]for (int k = head[x]; k; k = last[k]) if (!dfn[y]) {if (y != fa) dfs(y, x), ++child;low[x] = min(low[x], low[y]); if (!fa && child > 1 || fa && low[y] >= dfn[x]) f[x] = true;} else low[x] = min(low[x], dfn[y]);#undef y
}inline void kagari () {scanf("%d %d", &n, &m); for (int i = 1; i <= m; ++i) { int x, y; scanf("%d %d", &x, &y); addedge(x, y), addedge(y, x); }for (int i = 1; i <= n; ++i) if (!dfn[i]) dfs(i, 0);for (int i = 1; i <= n; ++i) if (f[i]) ++ans;printf("%d\n", ans); for (int i = 1; i <= n; ++i) if (f[i]) printf("%d ", i);return;
}

P8436 【模板】边双连通分量

和求桥类似,在 \(dfn_x==low_x\) 时栈顶到 \(x\) 都是一个边连通分量。

int n, m, ans;
std:: vector < std:: pair < int, int > > g[500003];
std:: vector < int > av[500003]; 
int dfn[500003], low[500003], cnt = 0;
std:: stack < int > s;
void dfs (int x, int last) {dfn[x] = low[x] = ++cnt; s.push(x);for (auto &i: g[x]) {if (i.second == (last ^ 1)) continue; // 不走回头路 if (!dfn[i.first]) {dfs(i.first, i.second);low[x] = std:: min(low[x], low[i.first]);}elselow[x] = std:: min(low[x], dfn[i.first]);}if (dfn[x] == low[x]) {++ans;while (s.size()) {int t = s.top(); s.pop();av[ans].push_back(t);if (t == x) break;}}
}inline void kagari () {scanf("%d %d", &n, &m);for (int i = 1; i <= m; ++i) {int x, y; scanf("%d %d", &x, &y);if (x == y) continue;g[x].push_back(std:: make_pair(y, i << 1)), g[y].push_back(std:: make_pair(x, i << 1 | 1)); // 注意是 2i 和 2i+1 }for (int i = 1; i <= n; ++i) if (!dfn[i]) dfs(i, 0); // 不需要再特判是否是孤立点了,因为在 dfs 时还会加上这个答案 printf("%d\n", ans);for (int i = 1; i <= ans; ++i) {printf("%d ", av[i].size());for (auto &j: av[i]) printf("%d ", j);puts("");} return;
}
http://www.jsqmd.com/news/464251/

相关文章:

  • WinRM连接失败?手把手教你用TrustedHosts解决Invoke-Command报错问题
  • AWS上给ALB配置错误率告警监控
  • 避坑指南:Windows本地开发环境搭建Jaeger+ES的完整流程
  • 问境AIST首发|以AI治理AI,悬镜原创多模态AIST新品发布
  • PCIe Capabilities List详解:如何通过链表结构管理硬件功能
  • PyTorch数据加载器shuffle参数详解:为什么训练集要打乱而验证集不用?
  • EME API与DRM技术:如何实现视频内容的防截屏与防录屏保护
  • AD24安装避坑指南:从下载到激活的完整流程(附常见问题解决)
  • 3,09
  • 告别强制重启!3种方法让Windows更新不再打扰你的工作(含PowerShell自动化方案)
  • Pixelium Design 更新:首版表格上线,完善表单、导航、反馈及视觉组件
  • YOLOv8损失函数实战:从IoU到Wise-IoU的替换与调优
  • MATLAB中vpasolve函数优化:多解策略在车辆漂移平衡态分析中的应用
  • 3.11 spring boot的自定义starter
  • Qwen3 32B大模型推理实战:vLLM与Docker高效部署指南
  • 老鸟整理,性能测试知多少?一篇带你彻底打通...
  • PCB设计软件选型指南:AD、Cadence与PADS的实战对比
  • Vivado工程瘦身指南:精准清理与关键文件保全策略
  • LLaMA-Factory性能优化秘籍:如何选择推理引擎与关键参数配置
  • LangChain 集成 Ollama:从基础对话到智能体构建实战
  • AHB协议实战解析:从流水线到突发传输的设计精髓
  • [RDK X5][实战]从零上手:地瓜机器人RDK X5的快速配置与核心功能验证
  • 【压测实战】Apifox动态变量与批量压测全流程解析
  • [FOC-Simulink] 永磁同步电机无传感矢量控制的IF强拖启动与平滑切换策略
  • F28003x CMPSS实战:手把手教你配置数字滤波器消除电源噪声(附代码)
  • S32K3 eMios输入捕获(SAIC模式)实现高精度信号周期测量的优化策略
  • Vivado IP核中的握手艺术:深入解析Blocking与NonBlocking模式对AXI4-Stream数据流的影响
  • 深入解析Docker Plugin:从基础概念到实战应用
  • 【汽车故障诊断2】从P0127到U0105:解码DTC标准协议与实战应用
  • VSCode远程开发避坑指南:SSH连接Docker容器完整配置流程(2023最新版)