首页 > 其他分享 >P4126 [AHOI2009] 最小割 题解

P4126 [AHOI2009] 最小割 题解

时间:2024-08-26 23:48:22浏览次数:10  
标签:tot AHOI2009 题解 最小 kMaxN kMaxM int low P4126

Description

A,B 两个国家正在交战,其中A国的物资运输网中有 \(N\) 个中转站,\(M\) 条单向道路。设其中第 \(i\ (1\leq i\leq M)\) 条道路连接了 \(u_i,v_i\) 两个中转站,那么中转站 \(u_i\) 可以通过该道路到达 \(v_i\) 中转站,如果切断这条道路,需要代价 \(c_i\)。

现在B国想找出一个路径切断方案,使中转站 \(s\) 不能到达中转站 \(t\),并且切断路径的代价之和最小。

小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题:

  • 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断?
  • 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?

现在请你回答这两个问题。

\(N\leq 4\times 10^3,M\times 6\times 10^4\)。

Solution

显然要先建图跑一遍最大流。

这时会发现残余网络上不是满流的边一定不为最小割上的边。因为让这条边的初始流量减去一个极小值后最大流结果不变,最小割也不变。但是如果这条边在任何一个最小割中就一定会让最小割减少,就矛盾了。所以结论是对的。

于是只有满流的边可能是最小割上的边,但是只有这个结论仍然无法判断一条边是否在最小割上。

注意到如果残余网络上存在一个环,那么把这个环上的初始流量均减 \(1\) 后最大流一定不变,所以这个环上的边也一定不为最小割边。

然后考虑缩点,只有缩点后 DAG 上的边可能为最小割边。

在这些边中只有直接连接 \(s\) 和 \(t\) 所在 scc 的边是必须边。

而对于 DAG 上其它边 \((u,v)\) 可以分别给出割和不割的构造:

  1. 割:让 \(s\to u\) 的路径为 \(A\) 集合,其余为 \(B\) 集合。
  2. 不割:如果 \(u\) 为 \(s\),则 \(v\) 一定不为 \(t\),让 \(s\to u\to v\) 的路径为 \(A\) 集合,其余为 \(B\) 集合。否则让 \(u\to v\to t\) 为 \(A\) 集合,其余为 \(B\) 集合。

所以 DAG 上的所有边均为可行边,连接 \(s\) 和 \(t\) 所在 scc 的边为必须边。

时间复杂度:\(O(N^2M)\)。

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 4e3 + 5, kMaxM = 2e5 + 5, kInf = 1e9;

int n, m, s, t, tot;
int u[kMaxM], v[kMaxM], w[kMaxM];
int dfn[kMaxN], low[kMaxN], bel[kMaxN];
bool ins[kMaxN], res[kMaxM][2];
std::vector<std::pair<int, int>> G[kMaxN];

namespace Dinic {
struct Edge {
  int v, w, pre;
} e[kMaxM * 2];

int tot = 1, flow, n, s, t;
int tail[kMaxN], cur[kMaxN], dep[kMaxN];

void adde(int u, int v, int w) { e[++tot] = {v, w, tail[u]}, tail[u] = tot; }
void add(int u, int v, int w) { adde(u, v, w), adde(v, u, 0); }
void clear() {
  for (int i = 0; i <= n; ++i) tail[i] = 0;
  for (int i = 0; i <= tot; ++i) e[i] = {0, 0, 0};
  flow = 0, tot = 1;
}
void init(int _n, int _s, int _t) { clear(); n = _n, s = _s, t = _t; }

bool bfs() {
  static bool vis[kMaxN] = {0};
  std::queue<int> q;
  for (int i = 1; i <= n; ++i)
    cur[i] = tail[i], dep[i] = 1e9, vis[i] = 0;
  q.emplace(s), dep[s] = 0, vis[s] = 1;
  for (; !q.empty();) {
    int u = q.front(); q.pop();
    if (u == t) return 1;
    for (int i = tail[u]; i; i = e[i].pre) {
      int v = e[i].v, w = e[i].w;
      if (w && !vis[v]) {
        dep[v] = dep[u] + 1, vis[v] = 1, q.emplace(v);
      }
    }
  }
  return 0;
}

int dfs(int u, int lim) {
  if (u == t || !lim) return lim;
  int flow = 0;
  for (int &i = cur[u]; i; i = e[i].pre) {
    int v = e[i].v, w = e[i].w;
    if (w && dep[v] == dep[u] + 1) {
      int fl = dfs(v, std::min(lim, w));
      if (!fl) dep[v] = 1e9;
      lim -= fl, flow += fl;
      e[i].w -= fl, e[i ^ 1].w += fl;
      if (!lim) break;
    }
  }
  return flow;
}

int maxflow() {
  int flow = 0;
  for (; bfs(); flow += dfs(s, kInf)) {}
  return flow;
}
} // namespace Dinic

void dfs(int u) {
  static int cnt = 0;
  static std::stack<int> stk;
  dfn[u] = low[u] = ++cnt, stk.emplace(u), ins[u] = 1;
  for (auto [v, id] : G[u]) {
    if (!dfn[v]) {
      dfs(v), low[u] = std::min(low[u], low[v]);
    } else if (ins[v]) {
      low[u] = std::min(low[u], dfn[v]);
    }
  }
  if (dfn[u] == low[u]) {
    ++tot;
    for (; !stk.empty();) {
      int t = stk.top(); stk.pop();
      bel[t] = tot, ins[t] = 0;
      if (t == u) break;
    }
  }
}

void dickdreamer() {
  std::cin >> n >> m >> s >> t;
  Dinic::init(n, s, t);
  for (int i = 1; i <= m; ++i) {
    std::cin >> u[i] >> v[i] >> w[i];
    Dinic::add(u[i], v[i], w[i]);
  }
  Dinic::maxflow();
  for (int i = 2; i <= Dinic::tot; ++i) {
    if (Dinic::e[i].w) {
      G[Dinic::e[i ^ 1].v].emplace_back(Dinic::e[i].v, i / 2);
    }
  }
  for (int i = 1; i <= n; ++i)
    if (!dfn[i])
      dfs(i);
  for (int i = 1; i <= m; ++i) {
    if (!Dinic::e[2 * i].w) {
      res[i][0] = (bel[u[i]] != bel[v[i]]);
      res[i][1] = (bel[u[i]] == bel[s] && bel[v[i]] == bel[t]);
      std::cout << res[i][0] << ' ' << res[i][1] << '\n';
    } else {
      std::cout << "0 0\n";
    }
  }
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:tot,AHOI2009,题解,最小,kMaxN,kMaxM,int,low,P4126
From: https://www.cnblogs.com/Scarab/p/18381717

相关文章

  • 最大矩阵区间 题解
    题意简述给定\(n\)行\(m\)列矩阵\(A\)。对于每一行\(i\),选择非空区间\([l_i,r_i]\),满足\(\foralli\in[1,n)\),\([l_i,r_i]\)和\([l_{i+1},r_{i+1}]\)相交,即\(\max\{l_i,l_{i+1}\}\leq\min\{r_i,r_{i+1}\}\)。求所有选出区间的\(A_{i,j}\)值......
  • Luogu P4588 数学运算 题解 [ 绿 ] [ 线段树 ]
    LuoguP4588数学运算。虽然是一个很典的题,但里面的思想还是比较值得记录的。假做法一开始看到此题还以为是乘法逆元的模板题,但看到\(m\)与\(M\)不互质,就知道这种做法是假的了。注意exgcd虽然能求模数为合数的逆元,但是要是两数不互质就什么算法都搞不了了。因此,本题不能......
  • Luogu P7250 BalticOI 山峰 题解 [ 蓝 ] [ 模拟 ] [ 并查集 ] [ BFS ]
    LuoguP7250BalticOI山峰。一道大模拟,很暴力,也很难写。建议紫或蓝,标签为模拟、广度优先搜索、并查集。思路首先观察到答案取决于路线上的最低点,所以我们可以把所有点的高度丢进一个桶里,从大到小枚举,尝试更新答案。这应该是个挺经典的trick了。感性理解可以看作所有山都先......
  • 题解:P9256 [PA 2022] Muzyka pop 2
    题解:P9256[PA2022]Muzykapop2题目传送门题目重点从前往后比较,和数字比较一样,如:12345<12445。如果一个串是另一个串的前缀,那么不是前缀串的那个字典序小。题目思路我爱贪心贪心就行了,每次让x增加1,找出1的个数来实现。要求序列是字典序最小的,因此每次选择尽可......
  • 动态dp——P8820 [CSP-S 2022] 数据传输 题解
    P8820[CSP-S2022]数据传输可怜的cnblog被(昨天DDos+今天CC)攻击了(望周知!),只好先发在CSDN题面:题目描述小C正在设计计算机网络中的路由系统。测试用的网络总共有nn......
  • 【跨域问题解决】Access to XMLHttpRequest at xxx from origin xxx has been blocked
    这个错误是由于浏览器的同源策略(CORS,Cross-OriginResourceSharing)导致的。当从一个源(origin)向另一个源请求资源时,如果这两个源的协议、域名或端口号不同,就会触发CORS策略。解决方法要解决这个问题,你需要在你的后端服务中添加CORS支持,以便它允许来自你的请求。这通常......
  • [ARC182C] Sum of Number of Divisors of Product 题解
    题目链接点击打开链接题目解法我怎么不会分治/fn首先把\(x\)分解成\(\prodp_i^{x_i}(0\lei\le5)\)的形式,正因数个数为\(\prod(x_i+1)\)有一个很牛的想法是:合并两个\(x_i\)序列(假设一个叫\(x_0,...,x_5\),另一个叫\(y_0,...,y_5\))先不考虑后面的\(+1\)(可以最后......
  • 题解:AT_arc183_b [ARC183B] Near Assignment
    题意:给你长度为\(N\)的整数序列\(A,B\)以及整数\(K\)。你可以执行下列操作零次或多次。选择整数\(i\)和\(j\)(\(1\leqi,j\leqN\))。这里,\(|i-j|\leqK\)必须保持不变。然后,将\(A_{i}\)的值改为\(A_{j}\)。判断是否有可能使\(A\)与\(B\)相同。分......
  • 常见问题解决 --- 如何给一个不支持配置代理的程序抓取https流量数据
    比如我有一个C#编写票务系统,它内嵌浏览器功能,我想抓取它的流量,但是这个客户端不支持配置代理设置解决办法:1.安装配置proxifier开启全局代理服务。安装好后网上有激活码激活一下,点击profile-proxyserver,添加一个代理服务器127.0.0.1,端口8080,协议https。点击profile-prox......
  • Java | Leetcode Java题解之第374题猜数字大小
    题目:题解:publicclassSolutionextendsGuessGame{publicintguessNumber(intn){intleft=1,right=n;while(left<right){//循环直至区间左右端点相同intmid=left+(right-left)/2;//防止计算时溢出......