「ZJOI2011」最小割

「ZJOI2011」最小割

传送门
建出最小割树,然后暴力计算任意两点之间最小割即可。
多组数据记得初始化。
参考代码:

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 160, __ = 3010 * 2 + 10, INF = 2147483647;

int tot = 1, head[_], Cap[__ << 1], Q[_];
struct Edge { int ver, cap, nxt; } edge[__ << 1];
inline void Add_edge(int u, int v, int d) { edge[++tot] = (Edge) { v, d, head[u] }, head[u] = tot ; }
inline void link(int u, int v, int d) { Add_edge(u, v, d), Cap[tot] = d, Add_edge(v, u, 0), Cap[tot] = 0; }

int n, m, dep[_], cur[_], p[_], t1[_], t2[_];
int num; struct node { int x, y, z; } G[_];
int fa[10][_], mn[10][_], ans[_][_];

inline int bfs(int s, int t) {
    int hd = 0, tl = 0;
    memset(dep, 0, sizeof dep);
    Q[++tl] = s, dep[s] = 1;
    while (hd < tl) {
        int u = Q[++hd];
        for (rg int i = head[u]; i; i = edge[i].nxt) {
            int v = edge[i].ver;
            if (dep[v] == 0 && edge[i].cap > 0)
                dep[v] = dep[u] + 1, Q[++tl] = v;
        }
    }
    return dep[t] > 0;
}

inline int dfs(int u, int flow, int t) {
    if (u == t) return flow;
    for (rg int& i = cur[u]; i; i = edge[i].nxt) {
        int v = edge[i].ver;
        if (dep[v] == dep[u] + 1 && edge[i].cap > 0) {
            int res = dfs(v, min(flow, edge[i].cap), t);
            if (res) { edge[i].cap -= res, edge[i ^ 1].cap += res; return res; }
        }
    }
    return 0;
}

inline int Dinic(int s, int t) {
    for (rg int i = 2; i <= tot; ++i) edge[i].cap = Cap[i];
    int res = 0;
    while (bfs(s, t)) {
        for (rg int i = 1; i <= n; ++i) cur[i] = head[i];
        while (int d = dfs(s, INF, t)) res += d;
    }
    return res;
}

inline void solve(int l, int r) {
    if (l == r) return ;
    int s = p[l], t = p[l + 1];
    G[++num] = (node) { s, t, Dinic(s, t) };
    int p1 = 0, p2 = 0;
    for (rg int i = l; i <= r; ++i) if (dep[p[i]]) t1[++p1] = p[i]; else t2[++p2] = p[i];
    for (rg int i = 1; i <= p1; ++i) p[l + i - 1] = t1[i];
    for (rg int i = 1; i <= p2; ++i) p[l + p1 + i - 1] = t2[i];
    solve(l, l + p1 - 1), solve(l + p1, r);
}

inline void dfs(int u, int f) {
    dep[u] = dep[f] + 1;
    for (rg int i = head[u]; i; i = edge[i].nxt) {
        int v = edge[i].ver; if (v == f) continue ;
        fa[0][v] = u, mn[0][v] = edge[i].cap, dfs(v, u);
    }
}

inline int calc(int x, int y) {
    int res = INF;
    if (dep[x] < dep[y]) swap(x, y);
    for (rg int i = 8; ~i; --i)
        if (dep[fa[i][x]] >= dep[y]) res = min(res, mn[i][x]), x = fa[i][x];
    if (x == y) return res;
    for (rg int i = 8; ~i; --i)
        if (fa[i][x] != fa[i][y]) res = min(res, min(mn[i][x], mn[i][y])), x = fa[i][x], y = fa[i][y];
    return min(res, min(mn[0][x], mn[0][y]));
}

inline void Main() {
    read(n), read(m);
    tot = 1, memset(head, 0, sizeof head);
    for (rg int u, v, d; m--; ) read(u), read(v), read(d), link(u, v, d), link(v, u, d);
    for (rg int i = 1; i <= n; ++i) p[i] = i;
    num = 0, solve(1, n);
    tot = 0, memset(head, tot, sizeof head);
    for (rg int i = 1; i <= num; ++i) Add_edge(G[i].x, G[i].y, G[i].z), Add_edge(G[i].y, G[i].x, G[i].z);
    fa[0][1] = 0, dfs(1, 0);
    for (rg int i = 1; i <= 8; ++i)
        for (rg int u = 1; u <= n; ++u)
            fa[i][u] = fa[i - 1][fa[i - 1][u]], mn[i][u] = min(mn[i - 1][u], mn[i - 1][fa[i - 1][u]]);
    for (rg int i = 1; i <= n; ++i) for (rg int j = i + 1; j <= n; ++j) ans[i][j] = calc(i, j);
    int q; read(q);
    for (rg int x, cnt; q--; ) {
        read(x), cnt = 0;
        for (rg int i = 1; i <= n; ++i) for (rg int j = i + 1; j <= n; ++j) cnt += ans[i][j] <= x;
        printf("%d\n", cnt);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    int T; read(T);
    while (T--) Main(), puts("");
    return 0;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12231713.html

时间: 2024-11-10 16:57:16

「ZJOI2011」最小割的相关文章

【BZOJ2229】【ZJOI2011】最小割

冷门知识点-- 原题: 小白在图论课上学到了一个新的概念--最小割,下课后小白在笔记本上写下了如下这段话: "对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而s,t的最小割指的是在关于s,t的割中容量最小的割" 现给定一张无向图,小白有若干个形如"图中有多少对点它们的最小割的容量不超过x呢"的疑问,小蓝虽然很

【BZOJ2229】【ZJOI2011】最小割 {没有错,这道题的算法跟题帽是一样的!!!}

题解:分治求最小割. [l--r]里任意找两个作为s.t(不妨把s设为l位置上的点,t设为r位置上的点)求最小割,两层for循环枚举修改map[i][j]即两点间最小割值. 然后一部分属于S集,一部分属于T集,分治[l,L],[R,r],每次求完最小割值都全局进行修改. 最后每次询问暴力做就好了,无需任何优化即可AC. 代码: #include <queue> #include <cstdio> #include <cstring> #include <iostr

LiberOJ #6007. 「网络流 24 题」方格取数 最小割 最大点权独立集 最大流

#6007. 「网络流 24 题」方格取数 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 在一个有 m×n m \times nm×n 个方格的棋盘中,每个方格中有一个正整数. 现要从方格中取数,使任意 2 22 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法. 输入格式 文件第 1 11 行有 2 22 个正整数 m mm 和 n nn,分别表示棋盘的行数和列数

「CQOI2016」不同的最小割

「CQOI2016」不同的最小割 传送门 建出最小割树,把每一个点对的最小割抠出来 \(\text{unique}\) 一下就好了. 参考代码: #include <algorithm> #include <cstring> #include <cstdio> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x".out&q

LG1344 「USACO4.4」Pollutant Control 最小割

问题描述 LG1344 题解 我太菜了,我一开始竟然没有看出这是个最小割裸题... 两个询问. 第一个询问,直接跑最小割就好了. 第二个询问,建图的时候边权建 \(1\) ,代表割掉这条边需要 \(1\) 的代价. \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; template <typename Tp> void read(Tp &x){ x=0;char ch=1;int fh; while

Codechef RIN 「Codechef14DEC」Course Selection 最小割离散变量模型

问题描述 提供中文版本好评,一直以为 Rin 是题目名字... pdf submit 题解 参考了 东营市胜利第一中学姜志豪 的<网络流的一些建模方法>(2016年信息学奥林匹克中国国家队候选队员论文) 读了之后很有感触,这里节选一段话: 最小割模型的本质是将点的集合 \(V\) 划分为两个点集 \(S,T\) ,使得 \(S \in S,T \in T\) ,且 \(S∩T=?\) 之前对最小割中边权为 \(INF\) 的边,一直理解为不允许被割,现在从另一个角度来认识,就是保证这条边所联通

LiberOJ #6002. 「网络流 24 题」最小路径覆盖

#6002. 「网络流 24 题」最小路径覆盖 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:Special Judge 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 给定有向图 G=(V,E) G = (V, E)G=(V,E).设 P PP 是 G GG 的一个简单路(顶点不相交)的集合.如果 V VV 中每个顶点恰好在 P PP 的一条路上,则称 P PP 是 G GG 的一个路径覆盖.P PP 中路径可以从 V VV 的任何一个顶点开始,

bzoj2229: [Zjoi2011]最小割(分治最小割+最小割树思想)

2229: [Zjoi2011]最小割 题目:传送门 题解: 一道非常好的题目啊!!! 蒟蒻的想法:暴力枚举点对跑最小割记录...绝对爆炸啊.... 开始怀疑是不是题目骗人...难道根本不用网络流???一看路牌....分治最小割?最小割树? 然后开始各种%论文... 简单来说吧,根据各种本蒟蒻不会证明的理论,那么:所有最小割都不是完全独立的,总共有n-1种(也就是树上的n-1条边)最小割 恰好和树的定义一样啊! 那么用一个solve递归函数来解决,一开始任意找两个点作为st和ed来最小割,然后分

[bzoj2229][Zjoi2011]最小割_网络流_最小割树

最小割 bzoj-2229 Zjoi-2011 题目大意:题目链接. 注释:略. 想法: 在这里给出最小割树的定义. 最小割树啊,就是这样一棵树.一个图的最小割树满足这棵树上任意两点之间的最小值就是原图中这两点之间的最小割. 这个性质显然是非常优秀的. 我们不妨这样假设,我么已经把最小割树求出来了,那么这个题就迎刃而解了. 我们可以直接枚举点对,然后暴力验证就可以直接枚举出所有的合法点对是吧. 那么问题来了,我们如何才能求出所有的合法的点对? 这就需要用到了最小割树的构建过程. 我们最小割树的构