[ZJOI2011]最小割 & [CQOI2016]不同的最小割 分治求最小割
题面:
题解:
其实这两道是同一道题。。。。
最小割是用的dinic,不同的最小割是用的isap
其实都是分治求最小割
简单讲讲思路吧
就是首先全部的点都在一个集合里,然后随意定两个点为s和t,这里默认是第一个和最后一个。
然后找到最小割,最小割将整张图分为了s集和t集,于是我们再用这个最小割更新跨集合点对之间的最小割。
这个很好理解,因为当前找到的最小割将s集和t集分开了,显然对于任意一组跨集合的点对而言,当前最小割都是一个可能的最小割。
然后我们再递归处理s集和t集(重复以上步骤)。
每次找到最小割后就更新跨集合点对。
本质上是分治吧。
之前看有些地方提到了最小割树,这里放个链接(这是我找到的写的最全的一篇了)
下面放代码吧,个人觉得看代码会更好理解,尤其是对分治不熟悉的人(比如我)
不同的最小割(isap):
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 900
#define ac 20000
#define inf 2139062143
#define D printf("line in %d\n", __LINE__);
char READ[], *o = READ;
int n, m, s, addflow, t, answer, tt;
int last[AC], c[AC], have[AC], a[AC], ans[AC][AC], good[AC];
int Head[AC], date[ac], Next[ac], haveflow[ac], tot = ;
int q[AC], head, tail;
int ss[], cnt;
bool z[AC]; inline int read()
{
int x = ; char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline void add(int f, int w, int S)
{
date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S;
date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = S;
//printf("%d ---> %d %d\n", f, w, S);
} inline void upmin(int &a, int b)
{
if(b < a) a = b;
} void pre()
{
int u, v, e;
n = read(), m = read();
for(R i = ; i <= n; i++) a[i] = i;
for(R i = ; i <= m; i++)
{
u = read(), v = read(), e = read();
add(u, v, e);
}
memset(ans, , sizeof(ans));
} bool bfs()
{
int x, now;
memset(have, , sizeof(have));
memset(c, , sizeof(c));
have[] = , c[t] = , x = t;
head = tail = ;
q[++tail] = t;
while(head < tail)
{
x = q[++head];
for(R i = Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && !c[now])
{
c[now] = c[x] + ;
++have[c[now]];//error...忘记统计了
q[++tail] = now;
}
}
}
memcpy(good, Head, sizeof(Head));
return c[s];
} inline void aru()
{
int x = t;
while(x != s)
{
haveflow[last[x]] -= addflow;
haveflow[last[x] ^ ] += addflow;
x = date[last[x] ^ ];
}
tt += addflow;
} void isap()
{
int x = s, now; bool done;
tt = , addflow = inf;
while(c[s] != )
{
if(x == t) aru(), addflow = inf, x = s;//忘记设置全局了,,,那在这里手动改一下吧
done = false;
for(R i = good[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && c[now] == c[x] - )
{
upmin(addflow, haveflow[i]);
done = true;
last[now] = i;
good[x] = i;
x = now;
break;
}
}
if(!done)
{
int go = ;
for(R i=Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && c[now]) upmin(go, c[now]);
}
good[x] = Head[x];
if(!(--have[c[x]])) break;
++have[c[x] = go + ];
if(x != s) x = date[last[x] ^ ];
}
}
} void restore()//还原
{
for(R i = ; i <= tot; i += )//对于无向图而言,这还是非常妙的
haveflow[i] = haveflow[i ^ ] = (haveflow[i] + haveflow[i ^ ]) / ;
} void dfs(int x)
{
int now;
z[x] = true;//....
for(R i = Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && !z[now]) dfs(now);
}
} void solve(int l, int r)
{
if(l == r) return ;
s = a[l], t = a[r];
restore();
//printf("%d %d\n", l, r);
bfs();//重新定层次
isap();
memset(z, , sizeof(z));
dfs(s);
for(R i=;i<=n;i++)//更新最小割
if(z[i])
for(R j=;j<=n;j++)
if(!z[j])
ans[i][j] = ans[j][i] = min(ans[i][j], tt);
int ll = l - , rr = r + ;
for(R i = l; i <= r; i++)
if(z[a[i]]) q[++ll] = a[i];
else q[--rr] = a[i];//不知道取什么名字了,先借这个用一下吧
for(R i = l; i <= r; i++) a[i] = q[i];
solve(l, ll), solve(rr, r);
} void work()
{
for(R i=;i<=n;i++)
for(R j=;j<i;j++)
ss[++cnt] = ans[i][j];
sort(ss + , ss + cnt + );
for(R i=;i<=cnt;i++)
if(ss[i] != ss[i + ]) ++answer;
printf("%d\n", answer);
} int main()
{
// freopen("in.in","r",stdin);
pre();
solve(, n);
work();
// fclose(stdin);
return ;
}
最小割(dinic):
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define inf 2139062143
#define getchar() *o++
#define AC 155
#define ac 101000
#define D printf("line in %d\n", __LINE__); char READ[], *o = READ;
int s, t, T, Q, n, m;
int last[AC], ans[AC][AC], a[AC], tmp[AC], c[AC];
int date[ac], Next[ac], haveflow[ac], Head[AC], tot;
int q[AC], head, tail;
bool z[AC]; inline int read()
{
int x = ;char c = getchar();bool z = false;
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
if(!z) return x;
else return -x;
} inline void add(int f, int w, int S)
{//因为是双向边,所以反向边就可以省掉了
date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, haveflow[tot] = S;
date[++tot] = f, Next[tot] = Head[w], Head[w] = tot, haveflow[tot] = S;
// printf("%d ---> %d %d\n", f, w, S);
} bool bfs()
{
int now, x;
head = tail = ;
memset(c, , sizeof(c));
c[s] = , q[++tail] = s;
while(head < tail)
{
x = q[++head];
for(R i = Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && !c[now])
{
c[now] = c[x] + ;
q[++tail] = now;
}
}
}
return c[t];
} int dfs(int x, int flow)
{
if(x == t) return flow;
int addflow, used = , now;
for(R i=Head[x]; i ; i = Next[i])
{
now = date[i];
if(c[now] == c[x] + )
{//流到下一个点去的流量在haveflow和剩余流量中取最小值
addflow = dfs(now, min(haveflow[i], flow - used));
haveflow[i] -= addflow;
haveflow[i^] += addflow;
used += addflow;
if(used == flow) return flow;
}
}
if(!used) c[x] = ;
return used;
} int dinic()
{
int rnt = ;
while(bfs()) rnt += dfs(s, inf);
return rnt;
} void DFS(int x)//标记能到的点,便于区分
{
int now;
z[x]= ;
for(R i = Head[x]; i ; i = Next[i])
{
now = date[i];
if(haveflow[i] && !z[now]) DFS(now);
}
} void restore()//挺妙的还原流量的方法,因为这两条边即是双向边,又可以当反向边
{//因此总流量肯定是不会变的,所以加起来取个平均值就相当于还原了
for(R i = ; i <= tot; i += )
haveflow[i] = haveflow[i+] = (haveflow[i] + haveflow[i+]) / ;
} void solve(int l, int r)
{
if(l == r) return ;
restore();
s = a[l], t = a[r];//随便选两个作为s和t
int tt = dinic();//跑最小割
memset(z, , sizeof(z));
DFS(s);//查看哪些点是能到的(即没有被割断)
for(R i=;i<=n;i++)//这里是在每次找到一个最小割的时候,就对跨集合点对的割进行更新,
if(z[i])//因此每次是需要枚举所有点对的。不然的话因为i是枚举的s集里面的东西,可能会导致某些点对没有被统计到
for(R j=;j<=n;j++)//获取跨集合点对的割的大小
if(!z[j]) ans[i][j] = ans[j][i] = min(ans[i][j], tt);
int ll = l - , rr = r + ;
for(R i = l; i <= r; i++)
if(z[a[i]])//如果这个点可以属于s集
tmp[++ll] = a[i];//加入左边(从左边开始塞)
else tmp[--rr] = a[i];//不然就放在右边
for(R i=l;i<=r;i++) a[i] = tmp[i];//类似于归并排序的,,,放回数组内
solve(l, ll), solve(rr, r);
} void pre()
{
int u, v, k;
tot = ;
memset(ans, , sizeof(ans));
memset(Head, , sizeof(Head));
n = read(), m = read();
for(R i=;i<=n;i++) a[i] = i;//先把点按顺序放进来
for(R i=;i<=m;i++)
{
u = read(), v = read(), k = read();
add(u, v, k);
}
solve(, n);//找出所有最小割
} void work()
{
int k, cnt;
Q = read();
while(Q--)
{
k = read(), cnt = ;
for(R i=;i<=n;i++)
for(R j=;j<i;j++)//不能一个点对重复统计了
if(ans[i][j] <= k) ++cnt;
printf("%d\n", cnt);
}
printf("\n");
} int main()
{
// freopen("in.in","r",stdin);
fread(READ, , , stdin);
T = read();
while(T--)
{
pre();
work();
}
// fclose(stdin);
return ;
}
[ZJOI2011]最小割 & [CQOI2016]不同的最小割 分治求最小割的更多相关文章
- (模板)hdoj1007(分治求平面最小点对)
题目链接:https://vjudge.net/problem/HDU-1007 题意:给定n个点,求平面距离最小点对的距离除2. 思路:分治求最小点对,对区间[l,r]递归求[l,mid]和[mid ...
- 【LuoguP3329&4123】[ZJOI2011]最小割&[CQOI2016]不同的最小割
链接1 链接2 题意简述 第一个题 : 问图中有多少不同的最小割数值 第二个题 : \(q\) 次询问图中多少对点对之间的最小割小于 \(x\) . Sol 两个都是模板题就放一起了. 求完最小割树直 ...
- 不同的最小割(cqoi2016,bzoj4519)(最小割树)
学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成 两个部分,如果结点\(s,t\)不在同一个部分中,则称这个划分是关于\(s,t\)的割.对于带权图来说,将 所有顶 ...
- 【bzoj2229】[Zjoi2011]最小割 分治+网络流最小割
题目描述 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分 ...
- Uvaoj 11248 Frequency Hopping(Dinic求最小割)
题意:1到n节点(节点之间有一定的容量),需要流过C的流量,问是否可以?如果可以输出possible, 否则如果可以扩大任意一条边的容量 可以达到目的,那么输出possible option:接着输出 ...
- hiho 第116周,最大流最小割定理,求最小割集S,T
小Hi:在上一周的Hiho一下中我们初步讲解了网络流的概念以及常规解法,小Ho你还记得内容么? 小Ho:我记得!网络流就是给定了一张图G=(V,E),以及源点s和汇点t.每一条边e(u,v)具有容量c ...
- poj 3469 Dual Core CPU【求最小割容量】
Dual Core CPU Time Limit: 15000MS Memory Limit: 131072K Total Submissions: 21453 Accepted: 9297 ...
- BZOJ_1001_狼抓兔子_(平面图求最小割+对偶图求最短路)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1001 1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec ...
- HDU - 3035 War(对偶图求最小割+最短路)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3035 题意 给个图,求把s和t分开的最小割. 分析 实际顶点和边非常多,不能用最大流来求解.这道题要用 ...
随机推荐
- ROS Twist和Odometry消息类型使用(Python)
消息类型: 1. Twist - 线速度角速度 通常被用于发送到/cmd_vel话题,被base controller节点监听,控制机器人运动 geometry_msgs/Twist geometry ...
- Linux 7.4配置VSFTP服务器
vsftpd(very secure ftp daemon,非常安全的FTP守护进程)是一款运行在Linux操作系统上的FTP服务程序,不仅完全开源而且免费,此外,还具有很高的安全性.传输速度,以及支 ...
- centos7系统配置系统用户基于ssh的google身份验证
最近也是服务器各种被入侵,所以在安全上,要万分注意,特此记录,借助google的身份验证插件,获取动态验证码完成ssh登陆. OS: centos7 安装配置: 1. 安装epel源 yum -y i ...
- PLSQL集合类型
PLSQL集合类型 --联合数组(索引表) /* 用于存储某个数据类型的数据集合类型 .通过索引获得联合数组中得值 如下例子: */ DECLARE CURSOR cur_chars IS SEL ...
- LabVIEW初篇---前言
最早接触labview,是研二的时候,2007年,当时为了补贴家用,改善生活.自己拿着本科毕业证去找工作,去一个企业面试,当时,面试的主考官,问了会什么吗,比如PLC.单片机啥的?那时候的自己,基本上 ...
- CodeForces - 1059D(二分+误差)
链接:CodeForces - 1059D 题意:给出笛卡尔坐标系上 n 个点,求与 x 轴相切且覆盖了所有给出点的圆的最小半径. 题解:二分半径即可.判断:假设当前二分到的半径是 R ,因为要和 x ...
- HashMap 阅读
最近研究了一下java中比较常见的map类型,主要有HashMap,HashTable,LinkedHashMap和concurrentHashMap.这几种map有各自的特性和适用场景.使用方法的话 ...
- mweb test
目录 Markdown syntax guide and writing on MWeb Philosophy Notice Headers This is an <h1> tag Thi ...
- Python高级编程-多线程
(一)进程线程概述: 很多同学都听说过,现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统. 什么叫“多任务”呢?简单地说,就是操作系统可以同时运行 ...
- Java微笔记(3)
Java 中的 static 使用之静态变量 Java 中被 static 修饰的成员称为静态成员或类成员. 它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享. 静态成员可以使用类名直接访 ...