BZOJ2229: [Zjoi2011]最小割(最小割树)-程序员宅基地

技术标签: 模板\算法\知识点总结  网络流  

传送门

最小割树

算法

初始时把所有点放在一个集合
从中任选两个点出来跑原图中的最小割
然后按照 s s s 集合与 t t t 集合的归属把当前集合划分成两个集合,递归处理
这样一共跑了 n − 1 n − 1 n1 次最小割
可以证明图中任意一对点之间的最小割的数值都包含在这 n − 1 n − 1 n1 个数值当中
把每次求出的最小割看成是两个点之间的边,可以建出一棵树

定理1

任意三点之间的最小割一定是两个相等的较小值和一个较大值

证明
设任意三点 a , b , c a, b, c a,b,c 之间的最小割分别为 m i n c u t ( a , b ) , m i n c u t ( a , c ) , m i n c u t ( b , c ) mincut(a, b), mincut(a, c), mincut(b, c) mincut(a,b),mincut(a,c),mincut(b,c)
m i n c u t ( a , b ) mincut(a, b) mincut(a,b) 是这三者中的最小值
那么在做 a − b a − b ab 割时, c c c 一定属于其中的某一个集合,设其与 a a a 同属于一个集合
那么就有 m i n c u t ( b , c ) ≤ m i n c u t ( a , b ) mincut(b, c) \le mincut(a, b) mincut(b,c)mincut(a,b)
又因为 m i n c u t ( a , b ) ≤ m i n c u t ( b , c ) mincut(a, b) \le mincut(b, c) mincut(a,b)mincut(b,c),所以两者相等

定理2

图中至多只有 n − 1 n − 1 n1 种不同的最小割

证明
编了一个构造的证明
首先根据定理 1 1 1 ,可以转化成如下问题:

n n n 个点的无向完全图,要给每一条边染色,要求每个三元环上有两条边同色,求最多可以染多少种颜色

考虑归纳法
假设现在已经有一条长度大于 1 1 1 的链 a a a b b b 上的边的颜色互不相同
考虑染 ( a , b ) (a,b) (a,b) 这条边
如果链长 = 2 =2 =2,那么 ( a , b ) (a,b) (a,b) 只能选择链上某条边的颜色
如果链长 > 2 >2 >2,假设两个端点在链上的不是 ( a , b ) (a,b) (a,b) 的边的颜色都是链上某条边的颜色
那么取出其中两条 ( a , c ) (a,c) (a,c) ( c , b ) (c,b) (c,b) ( a , b ) (a,b) (a,b) 只能选择两个中其中一条边的颜色,即链上某条边的颜色

所以可以得到,颜色互不相同的边不能形成环,所以最优的显然是树,即 n − 1 n-1 n1 中颜色

Sol

至此问题已经完美解决
建出最小割树,任意两个点的最小割就是路径边权最小值
直接暴力也可以
复杂度貌似 Θ ( n 3 m ) \Theta(n^3m) Θ(n3m) ? ? ? ??? ???

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn(505);
const int inf(1e9);

int first[maxn], n, m, cnt, lev[maxn], vis[maxn], s, t, id[maxn], tmp[maxn], cur[maxn], mincut[maxn][maxn], record[maxn << 4];
queue <int> q;

struct Edge {
    
    int to, next, w;
} edge[maxn << 4];

inline void Add(int u, int v, int w) {
    
    edge[cnt] = (Edge){
    v, first[u], w}, first[u] = cnt++;
    edge[cnt] = (Edge){
    u, first[v], w}, first[v] = cnt++;
    record[cnt - 1] = record[cnt - 2] = w;
}

inline int Bfs() {
    
    memset(lev, 0, sizeof(lev)), lev[s] = 1, q.push(s);
    register int u, e, v;
    while (!q.empty()) {
    
        for (u = q.front(), q.pop(), e = first[u]; ~e; e = edge[e].next)
            if (edge[e].w && !lev[v = edge[e].to]) lev[v] = lev[u] + 1, q.push(v);
    }
    return lev[t];
}

int Dfs(int u, int maxf) {
    
    if (u == t) return maxf;
    register int ret = 0, &e = cur[u], f, v;
    for (; ~e; e = edge[e].next)
        if (edge[e].w && lev[v = edge[e].to] == lev[u] + 1){
    
            f = Dfs(v, min(edge[e].w, maxf - ret));
            ret += f, edge[e].w -= f, edge[e ^ 1].w += f;
            if (ret == maxf) return ret;
        }
    if (!ret) lev[u] = 0;
    return ret;
}

inline int Dinic() {
    
    register int ret = 0, i;
    for (i = 0; i < cnt; ++i) edge[i].w = record[i];
    while (Bfs()) memcpy(cur, first, sizeof(cur)), ret += Dfs(s, inf);
    return ret;
}

void Mark(int u) {
    
    if (vis[u]) return;
    register int e;
    for (vis[u] = 1, e = first[u]; ~e; e = edge[e].next) if (edge[e].w) Mark(edge[e].to);
}

inline void Solve(int l, int r) {
    
    if (l >= r) return;
    memset(vis, 0, sizeof(vis)), s = id[l], t = id[r];
    register int i, j, mid, d = Dinic();
    for (Mark(s), j = 0, i = l; i <= r; ++i) if (vis[id[i]]) tmp[++j] = id[i];
    for (mid = l + j - 1, i = l; i <= r; ++i) if (!vis[id[i]]) tmp[++j] = id[i];
    for (i = l; i <= r; ++i) id[i] = tmp[i - l + 1];
    for (i = 1; i <= n; ++i)
        if (vis[i])
            for (j = 1; j <= n; ++j)
                if (i != j && !vis[j]) mincut[i][j] = mincut[j][i] = min(mincut[i][j], d);
    Solve(l, mid), Solve(mid + 1, r);
}

int main() {
    
    register int i, j, u, v, w, test;
    scanf("%d", &test);
    while (test--) {
    
        memset(first, -1, sizeof(first)), cnt = 0;
        scanf("%d%d", &n, &m);
        for (i = 1; i <= m; ++i) scanf("%d%d%d", &u, &v, &w), Add(u, v, w);
        for (i = 1; i <= n; ++i) id[i] = i;
        memset(mincut, 63, sizeof(mincut)), Solve(1, n), scanf("%d", &w);
        while (w--) {
    
            scanf("%d", &u), v = 0;
            for (i = 1; i <= n; ++i)
                for (j = i + 1; j <= n; ++j) v += mincut[i][j] <= u;
            printf("%d\n", v);
        }
        putchar('\n');
    }
    return 0;
}

参考文献

  1. 某鸽姓选手的网络流课件
    2. 垃圾博主自己 yy
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/oi_Konnyaku/article/details/84927936

智能推荐

网络安全入门 5 天速成教程_ WEB 安全渗透攻防技术-程序员宅基地

文章浏览阅读812次,点赞8次,收藏30次。希望能帮助大家尽快入门。

Mtk Camera Hal到驱动的流程(一)_mtk imageio-程序员宅基地

文章浏览阅读4.3k次,点赞7次,收藏78次。(1)架构介绍(A)Camera的框架分为Kernel部分和Hal部分Kernel部分:image sensor driver——负责具体型号的sensor的id检测,上电,以及在preview、capture、初始化、3A等等功能设定时的寄存器配置;ISP driver——通过DMA将sensor数据流上传;Hal部分:imageio——主要负责数据buffer上传的pipe;drv——包含imgsensor和isp的hal层控制;feature io——包含各种3A等性能配置;_mtk imageio

相机成像原理以及旋转_摄像机平移旋转的原理-程序员宅基地

摘要:相机成像原理涉及点在不同坐标系中的矩阵变换,包括世界、相机、像平面和像素平面坐标系。具体变换关系可用矩阵表达,可求得单位向量。

Android 13中的 Open Mobile API_sim trasmit apdu-程序员宅基地

文章浏览阅读4.4k次,点赞4次,收藏12次。SE 也就是 Secure Element,译为 “安全元素”主要应用场景在 手机手表交通卡、门禁、虚拟钱包、虚拟SIM卡,以及其他身份认证的且对安全级别有一定要求的业务。_sim trasmit apdu

【Flutter混编】InAppWebView常见配置&相册问题处理-程序员宅基地

文章浏览阅读5.1k次。Flutter自带WebView不想说啥了,就这样吧。反正一番周折之后选择使用第三方的InAppWebView。看源码可以看出本质上是用了Platform调回原生平台的webview,但是xing nen_inappwebview

springboot ControllerAdvice 类似上传超出文件大小异常无法捕获问题详解及解决方式_handlemaxuploadsizeexceededexception-程序员宅基地

文章浏览阅读6.7k次,点赞2次,收藏10次。spring boot/mvc通过@RestControllerAdvice或者@ControllerAdvice配合@ExceptionHandler实现全局异常统一处理在spring web项目开发中,我们经常会遇到各种exception,这些exception根据业务或者场景不同抛出不同的信息和返回类型,有的exception需要返回json数据格式的错误,有的exceptio..._handlemaxuploadsizeexceededexception

随便推点

计算机操作系统 实验四:进程通信(二)_消息缓冲队列。使用系统调用msgget ()、msgsnd ()、msgrcv ()及msgctl -程序员宅基地

文章浏览阅读9.1k次,点赞32次,收藏117次。1 .实验目的学习如何利用消息缓冲队列进行进程间的通信,并加深对消息缓冲队列通信机制的理解。2 .实验内容(1) 了解系统调用msgget()、msgsnd()、msgrcv()、msgctl()的功能和实现过程。(2) 编写一段程序,使其用消息缓冲队列来实现父进程和子进程之间的通信。父进程先建立一个关键字为MSGKEY(如75)(即#define MSGKEY 75)的消息队列,..._消息缓冲队列。使用系统调用msgget ()、msgsnd ()、msgrcv ()及msgctl () 编制消

如何使用mp4v2将H264+AAC裸流录制成mp4文件,并保持音视频同步【源码】【mp4】【录像】_mp4v2 同步音视频-程序员宅基地

文章浏览阅读6.5k次,点赞2次,收藏23次。前言: mp4文件目前已经成为了流媒体音视频行业的通用标准文件格式,它是基于mov格式基础上演变来的,特别适合多平台播放,录制一次,多个平台都可使用。但是,由于mp4格式相对比较复杂,直到mp4v2这个开源工程的出现,解决了这个问题。 通常,我们在使用mp4文件时,会遇到两个问题:如何从已有的mp4文件中抽取音视频数据帧;如何将音视频数据帧录制成mp4文件,并保持音视频同步。 上..._mp4v2 同步音视频

基于SpriteKit的游戏,如何添加界面-程序员宅基地

文章浏览阅读615次。使用UIKit开发界面众所周知,SpriteKit并不提供各种UI常见的组件,连基本的Button都没有,唯一有的文本显示组件Label还不支持多行。那么到底该用什么来为基于SpriteKit的游戏开发界面呢?可以肯定的回答:用UIKit。大家都这么想了,可是具体该怎么做,却不是很清楚。 如何把UIKit界面元素嵌入场景界面遇到的第一个问题是,如何把UIKit组件加入到场景中。UI

Scrapy实战:爬取知乎用户信息_运用分布式框架抓取知乎网站用户的信息,包括用户的姓名(昵称)、一句话介绍、部分-程序员宅基地

文章浏览阅读1.5k次,点赞2次,收藏8次。思路:从一个用户(本例为“张佳玮”)出发,来爬取其粉丝,进而爬取其粉丝的粉丝…先来观察网页结构:审查元素:可以看到用户“关注的人”等信息在网页中用json格式保存在data中。当把鼠标移到列表中的某个名字上时,可以看到浏览器产生了一个Ajax请求:请求的url后面加上了很长的一串查询字符串。并且json中也请求了许多详细的信息。这与该用户的主页基本是对应的:实战我们..._运用分布式框架抓取知乎网站用户的信息,包括用户的姓名(昵称)、一句话介绍、部分

23种设计模式——装饰者模式-程序员宅基地

文章浏览阅读3.9k次,点赞8次,收藏35次。文章目录23种设计模式——装饰者模式1、装饰者模式概述2、装饰者模式的结构3、装饰者模式的实现4、装饰者模式的应用场景23种设计模式——装饰者模式1、装饰者模式概述背景有些人为了早上多睡一会,就会用方便的方式解决早餐问题。有些人早餐可能会吃煎饼,煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。在现实生活中,常常需要对现有产品增加新的功能或美化其外观,如房子装修、相片加相框、咖啡加调料等,都是装饰器模式。装饰者模式的定义装饰者(Decorator)模式的定义:指在不改_装饰者模式

html css的参考文献,网页制作论文参考文献大全 网页制作参考文献有哪些-程序员宅基地

文章浏览阅读6.2k次。【100个】关于网页制作论文参考文献大全汇总,作为大学生的毕业生应该明白了网页制作参考文献有哪些,收集好参考文献后的网页制作论文写作起来会更轻松!一、网页制作论文参考文献范文[1]开展电脑社团活动培养学生信息素养——中学生网页制作社团活动报告.童宇阳,2009第九届中国教育信息化创新与发展论坛[2]项目教学法在《网页制作基础》课程中的应用.胡永刚,20072007无锡职教教师论坛[3]基于SVG的..._计算机网页制作相关参考文献

推荐文章

热门文章

相关标签