BZOJ 4435 [双连通分量][Hash]

本文介绍了一种针对旧果汁加工厂橙汁运输系统的流量分析方法。该系统由管道和节点构成,通过最大流最小割定理计算每对节点间最大流量的总和,并给出具体实现算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

你被雇佣升级一个旧果汁加工厂的橙汁运输系统。系统有管道和节点构成。每条管道都是双向的,且每条管道的流量都是1升每秒。管道可能连接节点,每个节点最多可以连接3条管道。节点的流量是无限的。节点用整数1n来表示。在升级系统之前,你需要对现有系统进行分析。对于两个不同节点stst的流量被定义为:当s为源点,t为汇点,从s能流向t的最大流量。以下面的第一组样例数据为例,16的流量为312的流量为2。计算每一对满足a<b的节点ab的流量的和。

Solution

根据最大流最小割定理,我们要求的其实就是最小割。题中所说每个节点只会连出至多三条边,显然最小割至多为3。分类讨论一下:
若最小割为0:说明两点之间不连通。
若最小割为1:说明两点处于不同的双连通分量中。
若最小割为23:考虑从图中删去一条边,若删去任何一条边之后两个点仍在同一双连通分量中,则说明最小割为3否则为2
如何判断删去任意一条边之后的双连通分量编号是否相同,只需要对每个点Hash一下。(注意要是有序的Hash)。
时间复杂度O(n2)

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

inline char get(void) {
  static char buf[100000], *S = buf, *T = buf;
  if (S == T) {
    T = (S = buf) + fread(buf, 1, 100000, stdin);
    if (S == T) return EOF;
  }
  return *S++;
}
inline void read(int &x) {
  static char c; x = 0;
  for (c = get(); c < '0' || c > '9'; c = get());
  for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

const int N = 3030;
const int M = 4545;
const int P = 2333333;
typedef long long ll;

struct edge {
  int to, next;
  edge (int t = 0, int n = 0):to(t), next(n) {}
};
edge G[M << 1];
int pre[N], low[N], bcc[N], sta[N];
int has[N], mark[M << 1], id[N];
int head[N];
int ans[N][N];
int n, m, x, y, Gcnt, clc, top, Bcnt, cnt, Ans;

inline int Min(int a, int b) {
  return a < b ? a : b;
}
inline void AddEdge(int from, int to) {
  G[++Gcnt] = edge(to, head[from]); head[from] = Gcnt;
  G[++Gcnt] = edge(from, head[to]); head[to] = Gcnt;
}
void dfs(int u, int fa) {
  low[u] = pre[u] = ++clc;
  int to; id[u] = cnt;
  sta[++top] = u;
  for (int i = head[u]; i; i = G[i].next)
    if (!mark[i]) {
      to = G[i].to;
      if (pre[to]) {
        if (to != fa) low[u] = Min(low[u], low[to]);
      } else if (to != fa) {
        dfs(to, u); low[u] = Min(low[u], low[to]);
      }
    }
  if (low[u] == pre[u]) {
    Bcnt++;
    while (sta[top] != u) {
      bcc[sta[top]] = Bcnt;
      top--;
    }
    bcc[sta[top--]] = Bcnt;
  }
}
void SetBcc(int flag = 0) {
  Bcnt = cnt = clc = top = 0;
  memset(pre, 0, sizeof pre);
  memset(id, 0, sizeof id);
  for (int i = 1; i <= n; i++)
    if (!pre[i]) {
      cnt++; dfs(i, 0);
    }
  if (flag) return (void)("%%%%gjghfd%%%%");
  for (int i = 1; i <= n; i++)
    has[i] = ((ll)has[i] * M % P + bcc[i]) % P;
}

int main(void) {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
  read(n); read(m);
  for (int i = 1; i <= m; i++) {
    read(x); read(y);
    AddEdge(x, y);
  }
  SetBcc(1);
  for (int i = 1; i <= n; i++)
    for (int j = i + 1; j <= n; j++) {
      if (bcc[i] != bcc[j]) ans[i][j] = 1;
      if (id[i] != id[j]) ans[i][j] = -1;
    }
  for (int i = 1; i <= Gcnt; i += 2) {
    mark[i] = mark[i + 1] = 1;
    SetBcc();
    mark[i] = mark[i + 1] = 0;
  }
  for (int i = 1; i <= n; i++)
    for (int j = i + 1; j <= n; j++) {
      if (ans[i][j] != 0) continue;
      if (has[i] == has[j]) ans[i][j] = 3;
      else ans[i][j] = 2;
    }
  for (int i = 1; i <= n; i++)
    for (int j = i + 1; j <= n; j++)
      if (ans[i][j] > 0) Ans += ans[i][j];
  cout << Ans << endl;
  return 0;
}
### 关于 BZOJ1728 Two-Headed Cows (双头牛) 的算法解析 此问题的核心在于如何通过有效的图论方法解决给定约束下的最大独立集问题。以下是详细的分析和解答。 #### 问题描述 题目要求在一个无向图中找到最大的一组节点集合,使得这些节点之间满足特定的颜色匹配条件。具体来说,每条边连接两个节点,并附带一种颜色标记(A 或 B)。对于任意一条边 \(u-v\) 和其对应的颜色 \(c\),如果这条边属于最终选取的子集中,则必须有至少一个端点未被选入该子集或者两端点均符合指定颜色关系。 #### 解决方案概述 本题可以通过 **二分枚举 + 图染色验证** 来实现高效求解。核心思想如下: 1. 假设当前最优解大小为 \(k\),即尝试寻找是否存在一个大小为 \(k\) 的合法子集。 2. 枚举每一个可能作为起点的节点并将其加入候选子集。 3. 对剩余部分执行基于 BFS/DFS 的图遍历操作,在过程中动态调整其他节点的状态以确保整体合法性。 4. 如果某次试探能够成功构建符合条件的大规模子集,则更新答案;反之则降低目标值重新测试直至收敛至最佳结果。 这种方法利用了贪心策略配合回溯机制来逐步逼近全局最优点[^1]。 #### 实现细节说明 ##### 数据结构设计 定义三个主要数组用于记录状态信息: - `color[]` : 存储每个顶点所分配到的具体色彩编号; - `used[]`: 表示某个定点是否已经被处理过; - `adjList[][]`: 记录邻接表形式表示的原始输入数据结构便于后续访问关联元素。 ##### 主要逻辑流程 ```python from collections import deque def check(k, n): def bfs(start_node): queue = deque([start_node]) used[start_node] = True while queue: u = queue.popleft() for v, c in adjList[u]: if not used[v]: # Assign opposite color based on edge constraint 'c' target_color = ('B' if c == 'A' else 'A') if color[u]==c else c if color[v]!=target_color and color[v]!='?': return False elif color[v]=='?': color[v]=target_color queue.append(v) used[v] =True elif ((color[u]==c)==(color[v]==('B'if c=='A'else'A'))): continue return True count=0 success=True for i in range(n): if not used[i]: temp_count=count+int(color[i]=='?' or color[i]=='A') if k<=temp_count: color_copy=color[:] if bfs(i): count=temp_count break else : success=False return success n,m=list(map(int,input().split())) colors=[['?']*m]*n for _ in range(m): a,b,c=input().strip().split() colors[int(a)-1].append((int(b),c)) low ,high,res=0,n,-1 while low<=high: mid=(low+high)//2 color=['?']*n used=[False]*n if check(mid,n): res=mid low=mid+1 else : high=mid-1 print(res) ``` 上述代码片段展示了完整的程序框架以及关键函数 `check()` 的内部运作方式。它接受参数 \(k\) 并返回布尔值指示是否有可行配置支持如此规模的选择[^2]。 #### 复杂度分析 由于采用了二分查找技术缩小搜索空间范围再加上单轮 DFS/BFS 时间复杂度 O(V+E),总体性能表现良好适合大规模实例运行需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值