[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分开的最小割. 分析 实际顶点和边非常多,不能用最大流来求解.这道题要用 ...
随机推荐
- spring源码-Aware-3.4
一.Aware接口,这个也是spring的拓展之一,为啥要单独拿出来讲呢,因为他相比于BeanFactoryPostProcessor,BeanPostProcessor的实用性更加高,并且在具体的业 ...
- autocomplete.jquery 点击或进入默认显示所有结果
注意使用的是autocomplete.jquery,官网地址是:https://github.com/devbridge/jQuery-Autocomplete.而不是JqueryUI的autocom ...
- Myeclipse - 问题集 - specified vm install not found
In Eclipse, click the ant file -- Run As -- External Tools Configuration and click on the JRE tab. S ...
- angular ng-bind-html $sce.trustAsHtml
使用ng-bind-html和$sce.trustAsHtml显示有html符号的内容 angularjs的强大之处之一在于它的双向数据绑定的功能,我们通常会使用data-ng-bind或者dat ...
- MySQL不能连接本地数据库10061
可能的原因是本地服务器没有启动,在安装配置MySQL时,我去掉了开机自动开启,所以开机之后出现了错误10061 解决办法: 一.计算机右击选择管理 二.选择服务,找到MySQL,右击手动,选择启动服务
- XenServer master主机的作用
https://wenku.baidu.com/view/a2d3f0a333d4b14e852468c9.html###
- 「题目代码」P1044~P1048(Java)
P1044 谭浩强C语言(第三版)习题5.8 import java.util.*; import java.io.*; import java.math.BigInteger; public cla ...
- OSG-HUD
本文转至http://www.cnblogs.com/shapherd/archive/2010/08/10/osg.html 作者写的比较好,再次收藏,希望更多的人可以看到这个文章 互联网是是一个相 ...
- Python全栈 Web(边框、盒模型、背景)
原文地址 https://yq.aliyun.com/articles/634926 ......................................................... ...
- [HNOI2017]大佬
参考题解 \(\text{Solution}\) 我们发现5个行为中2操作与其它操作无关,所以我们采用贪心,尽量让多的时间去攻击大佬. 设 \(f[i][j]\) 表示前 \(i\) 天剩 \(j\) ...