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

最小割 bzoj-2229 Zjoi-2011

题目大意:题目链接

注释:略。



想法:

在这里给出最小割树的定义。

最小割树啊,就是这样一棵树。一个图的最小割树满足这棵树上任意两点之间的最小值就是原图中这两点之间的最小割。

这个性质显然是非常优秀的。

我们不妨这样假设,我么已经把最小割树求出来了,那么这个题就迎刃而解了。

我们可以直接枚举点对,然后暴力验证就可以直接枚举出所有的合法点对是吧。

那么问题来了,我们如何才能求出所有的合法的点对?

这就需要用到了最小割树的构建过程。

我们最小割树的构建方式是分治构建的。

也就是说:

我们每次直接随意取出两个点然后在原图中求出这两个点的最小割。

并且在这两个点之间连一条等于最小割大小的边。

之后我们对于原图把所有和S相连的分到一侧,把所有和T相连的分到另一侧。

递归分治即可。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define inf 0x3f3f3f3f
#define N 155
using namespace std;

int cnt,n,m,dis[N],last[N],a[N],tmp[N],ans[N][N],s,t,mark[N];
struct edge{int to,c,next;}e[N*200];
queue <int> q;

void addedge(int u,int v,int c)
{
	e[++cnt].to=v;e[cnt].c=c;e[cnt].next=last[u];last[u]=cnt;
	e[++cnt].to=u;e[cnt].c=c;e[cnt].next=last[v];last[v]=cnt;
}

bool bfs()
{
	memset(dis,0,sizeof(dis));
	dis[s]=2;
	while (!q.empty()) q.pop();
	q.push(s);
	while (!q.empty())
	{
		int u=q.front();
		q.pop();
		for (int i=last[u];i;i=e[i].next)
			if (e[i].c&&!dis[e[i].to])
			{
				dis[e[i].to]=dis[u]+1;
				if (e[i].to==t) return 1;
				q.push(e[i].to);
			}
	}
	return 0;
}

int dfs(int x,int maxf)
{
	if (x==t||!maxf) return maxf;
	int ret=0;
	for (int i=last[x];i;i=e[i].next)
		if (e[i].c&&dis[e[i].to]==dis[x]+1)
		{
			int f=dfs(e[i].to,min(e[i].c,maxf-ret));
			e[i].c-=f;
			e[i^1].c+=f;
			ret+=f;
			if (ret==maxf) break;
		}
	if (!ret) dis[x]=0;
	return ret;
}
void dfs(int x)
{
	mark[x]=1;
	for (int i=last[x];i;i=e[i].next)
		if (e[i].c&&!mark[e[i].to]) dfs(e[i].to);
}
void solve(int l,int r)
{
	if (l==r) return;
	s=a[l];t=a[r];
	for (int i=2;i<=cnt;i+=2)
		e[i].c=e[i^1].c=(e[i].c+e[i^1].c)/2;
	int flow=0;
	while (bfs()) flow+=dfs(s,inf);
	memset(mark,0,sizeof(mark));
	dfs(s);
	for (int i=1;i<=n;i++)
		if (mark[i])
			for (int j=1;j<=n;j++)
				if (!mark[j])
					ans[i][j]=ans[j][i]=min(ans[i][j],flow);
	int i=l,j=r;
	for (int k=l;k<=r;k++)
		if (mark[a[k]]) tmp[i++]=a[k];
		else tmp[j--]=a[k];
	for (int k=l;k<=r;k++)
		a[k]=tmp[k];
	solve(l,i-1);
	solve(j+1,r);
}

int main()
{
	int cas;
	scanf("%d",&cas);
	while (cas--)
	{
		scanf("%d%d",&n,&m);
		cnt=1;
		for (int i=1;i<=n;i++)
			a[i]=i;
		memset(last,0,sizeof(last));
		memset(ans,inf,sizeof(ans));
		for (int i=1;i<=m;i++)
		{
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			addedge(x,y,z);
		}
		solve(1,n);
		int q;
		scanf("%d",&q);
		for (int i=1;i<=q;i++)
		{
			int x,tot=0;
			scanf("%d",&x);
			for (int i=1;i<n;i++)
				for (int j=i+1;j<=n;j++)
					if (ans[i][j]<=x) tot++;
			printf("%d\n",tot);
		}
		cout<<endl;
	}
	return 0;
}

小结:好东西啊。

原文地址:https://www.cnblogs.com/ShuraK/p/10244898.html

时间: 2024-10-20 23:48:10

[bzoj2229][Zjoi2011]最小割_网络流_最小割树的相关文章

[bzoj1565][NOI2009]植物大战僵尸_网络流_拓扑排序

植物大战僵尸 bzoj1565 题目大意:给你一张网格图,上面种着一些植物.你从网格的最右侧开始进攻.每个植物可以对僵尸提供能量或者消耗僵尸的能量.每个植物可以保护一个特定网格内的植物,如果一个植物被保护,那么如果僵尸想吃掉该植物就必须先吃掉保护它的植物.问:僵尸最多能获得多少能量. 注释:1<=N(网格的宽)<=20,1<=M(网格的长)<=30,-20,000<=代价和收益<=20,000. 想法:前置题目([NOI2006]最大获利).这道题和最大获利比较相像,如

HDU 3046Pleasant sheep and big big wolf(网络流之最小割)

题目地址:HDU 3046 最小割第一发!其实也没什么发不发的...最小割==最大流.. 入门题,但是第一次入手最小割连入门题都完全没思路...sad..对最小割的本质还是了解的不太清楚.. 这题就是对每两个相邻的格子的边界都要进行加边,然后求最大流就OK了. RE了好长时间,注意遍历加边的时候要从1开始,而不是0开始,因为0是源点的...(也许只有我才犯这种错误吧...)建图不多说了..只要了解了最小割,建图还是很容易想的. 代码如下: #include <iostream> #includ

数据挖掘-diabetes数据集分析-糖尿病病情预测_线性回归_最小平方回归

     [python] view plain copy print? # coding: utf-8 # 利用 diabetes数据集来学习线性回归 # diabetes 是一个关于糖尿病的数据集, 该数据集包括442个病人的生理数据及一年以后的病情发展情况. # 数据集中的特征值总共10项, 如下: # 年龄 # 性别 #体质指数 #血压 #s1,s2,s3,s4,s4,s6  (六种血清的化验数据) #但请注意,以上的数据是经过特殊处理, 10个数据中的每个都做了均值中心化处理,然后又用

托盘图标_实现方法_最小化到托盘_关闭到托盘_delphi_托盘汇总

托盘图标_实现方法_最小化到托盘_关闭到托盘_delphi_托盘汇总 转自:http://midasyang.blog.163.com/blog/static/189744104201141245344681/ 启动不显示主窗体如果通过Form1.Visible:=False;语句来实现的话,会出现主窗体一闪而过的情形.更好的办法是在OnShow事件中添加Application.ShowMainForm := False; 程序不出现在任务栏procedure TForm1.FormShow(S

POJ 3469 Dual Core CPU(网络流之最小割)

题目地址:POJ 3469 建图思路:建源点与汇点,源点与CPU1相连,汇点与CPU2相连,对共享数据的之间连无向边. 我的ISAP过这题还是毫无时间压力的嘛... 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #include <math.h> #include <ctyp

ZOJ 3792 Romantic Value(网络流之最小割)(找割边)

题目地址:ZOJ 3792 最小割做的太少..这题很明显是找割边.找割边就是判断正向弧是否是0.如果跑完一次最小割后正向弧流量为0的话,那就说明这个边为一条割边.但是找到了割边后再怎么办呢..中午睡觉的时候突然来了灵感..再利用这些割边求一次最大流不就行了..把割边的流量都设为1,其他的都为正无穷.那最后的流量就是最少需要的割边了.然后计算就可以了. 这次又把上限值直接设为sink+1了...导致WA了12发.....sad...以后得注意... 代码如下: #include <iostream

【CF331E】Biologist(网络流,最小割)

[CF331E]Biologist(网络流,最小割) 题面 洛谷 翻译: 有一个长度为\(n\)的\(01\)串,将第\(i\)个位置变为另外一个数字的代价是\(v_i\). 有\(m\)个要求 每个要求的形式是 首先确定若干位置都要是\(0\)或者\(1\) 然后给定这\(K\)个位置,如果些位置上都满足要求 那么就可以得到\(W_k\)元 某些要求如果失败了还要倒着给\(g\)元 问最终能够得到的最大利润 输入格式: 第一行是\(n,m,g\) 第二行是\(V_i\) 接下来\(m\)行 第

二者取其一(初遇)_网络流

二者取其一,就是一堆物品,放入两个集合内,放进不同的集合内就会有不同的收益(或代价),使其收益(代价)最大(最小)的一种问题 通常这类问题,使用最小割定理解决. 最小割,即割边集中权值之和最小的一个集合 比如这道题[SHOI2007]善意的投票 是一道这样类型的题. 我们将\(S\)点设为同意睡觉的超级源点\(T\)设为不同意睡觉的点,同以对好朋友之间连一条容量为1的无向边,这样建图.我们求得一个最小割,就是答案的解了 不过这里有一个与其他网络流不同的地方,就是朋友间为什么要建立无向边,而不是有

hdoj 3251 Being a Hero 【建图后求解最小割 + 输出任意一组最小割里面边 的编号】

Being a Hero Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1252    Accepted Submission(s): 395 Special Judge Problem Description You are the hero who saved your country. As promised, the ki