BZOJ 2521: [Shoi2010]最小生成树(最小割)

题意

对于某一条无向图中的指定边 (a,b) , 求出至少需要多少次操作。可以保证 (a,b) 边在这个无向图的最小生成树中.

一次操作指: 先选择一条图中的边 (u,v), 再把图中除了这条边以外的边, 每一条的权值都减少 1 .

n500,m800,1wi<106

题解

给除了一条边的所有边权 1 ,相当于给这条边的边权 +1

利用生成树的结论,一条边 uv (权值 w )若要必定存在于生成树中,那么 uv 所有路径上存在一条边的边权都不能大于 w

那么我们令这些边的权值为 wi=max{w(a,b)wi+1,0}

然后从 ab 跑一遍最小割就行了。

这样为什么是对的呢?因为只要割掉了所有可行的边,那么就把 a,b 分成两个连通子图,要连上这两个子图的最小代价就是这个最小割的答案。

总结

对于一些奇怪数据范围的题,可以向网络流上想。

代码

注意连边的时候,需要把正向边和反向边的流量都置为正,这是因为整个图是无向的。

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << x << endl #define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("2521.in", "r", stdin); freopen ("2521.out", "w", stdout); #endif } const int N = 510, M = 810 * 2, inf = 0x3f3f3f3f; int n, m, id; namespace Dinic { int Head[N], Next[M], to[M], cap[M], e = 1; inline void add_edge(int u, int v, int flow) { to[++ e] = v; Next[e] = Head[u]; Head[u] = e; cap[e] = flow; } inline void Add(int u, int v, int flow) { add_edge(u, v, flow); add_edge(v, u, flow); } int S, T, dis[N]; bool Bfs() { queue<int> Q; Set(dis, 0); Q.push(S); dis[S] = 1; while (!Q.empty()) { int u = Q.front(); Q.pop(); for (int i = Head[u]; i; i = Next[i]) if (cap[i]) { int v = to[i]; if (!dis[v]) { dis[v] = dis[u] + 1; if (v == T) return true; Q.push(v); } } } return false; } int cur[N]; int Dfs(int u, int flow) { if (u == T || !flow) return flow; int res = 0, f; for (int &i = cur[u]; i; i = Next[i]) if (cap[i]) { int v = to[i]; if (dis[v] != dis[u] + 1) continue ; if ((f = Dfs(v, min(flow, cap[i])))) { cap[i] -= f; cap[i ^ 1] += f; res += f; if (!(flow -= f)) break; } } if (!flow || !res) dis[u] = 0; return res; } int Run() { int sum_flow = 0; while (Bfs()) { Cpy(cur, Head); sum_flow += Dfs(S, inf); } return sum_flow; } } using namespace Dinic; struct Edge { int u, v, w; } lt[M]; int main () { File(); n = read(); m = read(); id = read(); For (i, 1, m) { int u = read(), v = read(), w = read(); lt[i] = (Edge){u, v, w}; } S = lt[id].u; T = lt[id].v; For (i, 1, m) if (id != i) { int res = max(lt[id].w - lt[i].w + 1, 0); if (res) Add(lt[i].u, lt[i].v, res); } printf ("%d\n", Run()); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9556688.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(246)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示