[ZJOI2011]最小割(最小割树模板)

https://www.luogu.com.cn/problem/P3329

最小割树的用处不仅是做这些裸题,了解这个定理,会对一类问题有更深的思考。

最小割树的实现:

每次取两个点u,v,求它们的割,并在最小割树上给它们连边,权值为这个割。

然后按照S能走到的和能走到T的,分成两类点,继续递归建树。

原图中的两个点的最小割,即为树上边权的最小值。

容易证明最小割<=树上边权的最小值,但是要证明恰好是,比较困难,博主暂时不会。

Code:


#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int N = 6005;

int n, m;
int x, y, z;

int fi[N], to[N], nt[N], r[N], tot = 1;

void link(int x, int y, int z) {
	nt[++ tot] = fi[x], to[tot] = y, r[tot] = z, fi[x] = tot;
	nt[++ tot] = fi[y], to[tot] = x, r[tot] = z, fi[y] = tot;
}

int dis[N], d[N], d0, S, T;

int bfs() {
	fo(i, 0, n) dis[i] = 1e9;
	dis[S] = 0, d[d0 = 1] = S;
	for(int i = 1; i <= d0; i ++) {
		int x = d[i];
		for(int j = fi[x]; j; j = nt[j]) if(r[j]) {
			int y = to[j];
			if(dis[y] == 1e9) {
				dis[y] = dis[x] + 1;
				d[++ d0] = y;
			}
		}
	}
	return dis[T] != 1e9;
}

int dg(int x, int flow) {
	if(x == T) return flow;
	int use = 0;
	for(int i = fi[x]; i; i = nt[i]) if(r[i] && dis[x] + 1 == dis[to[i]]) {
		int t = dg(to[i], min(flow - use, r[i]));
		use += t, r[i] -= t, r[i ^ 1] += t;
		if(use == flow) return use;
	}
	return use;
}

int mark[N];

void dfs(int x) {
	if(mark[x]) return;
	mark[x] = 1;
	for(int i = fi[x]; i; i = nt[i]) if(r[i])
		dfs(to[i]);
}

#define V vector<int>
#define pb push_back
#define re resize
#define si size()

int ans[N][N];

void cl() {
	for(int i = 2; i <= tot; i += 2) {
		int s = r[i] + r[i ^ 1];
		r[i] = r[i ^ 1] = s / 2;
	}
}

void dg(V a) {
	if(a.si <= 1) return;
	cl();
	S = a[0], T = a[1];
	int sum = 0;
	while(bfs()) sum += dg(S, 1 << 30);
	fo(i, 1, n) mark[i] = 0;
	dfs(S);
	fo(i, 1, n) if(mark[i])
		fo(j, 1, n) if(!mark[j]) {
			if(sum < ans[i][j])
				ans[i][j] = ans[j][i] = sum;
		}
	V b, c; b.clear(); c.clear();
	ff(i, 0, a.si) if(mark[a[i]])
		b.pb(a[i]); else c.pb(a[i]);
	dg(b); dg(c);
}

void cl_e() {
	fo(i, 1, n) fi[i] = 0;
	tot = 1;
}

int main() {
	int T;
	scanf("%d", &T);
	fo(iT, 1, T) {
		cl_e();
		scanf("%d %d", &n, &m);
		fo(i, 1, m) {
			scanf("%d %d %d", &x, &y, &z);
			link(x, y, z);
		}
		fo(i, 1, n) fo(j, 1, n) ans[i][j] = 1e9;
		V a; a.re(n);
		fo(i, 1, n) a[i - 1] = i;
		dg(a);
		int q, x;
		scanf("%d", &q);
		fo(ii, 1, q) {
			scanf("%d", &x);
			int s = 0;
			fo(i, 1, n) fo(j, i + 1, n)
				s += ans[i][j] <= x;
			pp("%d\n", s);
		}
		hh;
	}
}

原文地址:https://www.cnblogs.com/coldchair/p/12636739.html

时间: 2024-11-08 12:04:05

[ZJOI2011]最小割(最小割树模板)的相关文章

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

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

scu - 3254 - Rain and Fgj(最小点权割)

题意:N个点.M条边(2 <= N <= 1000 , 0 <= M <= 10^5),每一个点有个权值W(0 <= W <= 10^5),现要去除一些点(不能去掉点0),使得结点 0 与结点 N - 1 不连通,求去掉的点的最小权值和. 题目链接:http://cstest.scu.edu.cn/soj/problem.action?id=3254 -->>这是很明显的最小点权割.. 建图方案: 1)将全部点 i 拆成 i 和 i + N.i ->

3532: [Sdoi2014]Lis 最小字典序最小割

3532: [Sdoi2014]Lis Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 865  Solved: 311[Submit][Status][Discuss] Description 给定序列A,序列中的每一项Ai有删除代价Bi和附加属性Ci.请删除若干项,使得4的最长上升子序列长度减少至少1,且付出的代价之和最小,并输出方案.如果有多种方案,请输出将删去项的附加属性排序之后,字典序最小的一种. 这题难点在如何求一组最小字典序最小的最小

ACM/ICPC 之 Dinic+枚举最小割点集(可做模板)(POJ1815)

最小割的好题,可用作模板. //Dinic+枚举字典序最小的最小割点集 //Time:1032Ms Memory:1492K #include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<queue> using namespace std; #define MAXN 205 #define INF 0x3f3f3f3f int N, S, T

关于最小代价子母树

第一次尝试写动态规划(Dynamic Planning)= 问题如下: ------------------------------------------------------------------------------------------------------------------------- 最小代价子母树 设有一排数,共n个,例如:22 14 7 13 26 15 11.任意2个相邻的数可以进行归并,归并的代价为该两个数的和,经过不断的归并,最后归为一堆,而全部归并代价的

[ACM] 线段树模板

#include<iostream> #include<cmath> using namespace std; #define maxn 200005 class Node{ public: int l,r; int add;//附加值 int sum; }node[maxn]; int getRight(int n){//获得满足2^x>=n的最小x[从0层开始,给编号获得层数] return ceil(log10(n*1.0)/log10(2.0)); } void bu

[WC2008]游览计划 「斯坦那树模板」

斯坦那树 百度释义 斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种.最小生成树是在给定的点集和边中寻求最短网络使所有点连通.而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小. 即最小斯坦那树即为并非选择所有的结点,而是选择一部分结点,为保证它们连通,且求解最小开销 题解 斯坦那树模板 发现直接表示点的存在性没有意义 设函数 \(f[i][state]\) 表示:对于点 \(i\),其它结点与其连通情况 那么有两种转移 其一.由其子集转移 \[f[i][state

poj3630 Phone List (trie树模板题)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26328   Accepted: 7938 Description Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogu

HDU 1251 Trie树模板题

1.HDU 1251 统计难题  Trie树模板题,或者map 2.总结:用C++过了,G++就爆内存.. 题意:查找给定前缀的单词数量. #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b #define F(i,a,b

【POJ 2104】 K-th Number 主席树模板题

达神主席树讲解传送门:http://blog.csdn.net/dad3zz/article/details/50638026 2016-02-23:真的是模板题诶,主席树模板水过.今天新校网不好,没有评测,但我立下flag这个代码一定能A.我的同学在自习课上考语文,然而机房党都跑到机房来避难了\(^o^)/~ #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(i