题面:

[ZJOI2011]最小割

[CQOI2016]不同的最小割

题解:

其实这两道是同一道题。。。。

最小割是用的dinic,不同的最小割是用的isap

其实都是分治求最小割

简单讲讲思路吧

就是首先全部的点都在一个集合里,然后随意定两个点为s和t,这里默认是第一个和最后一个。

然后找到最小割,最小割将整张图分为了s集和t集,于是我们再用这个最小割更新跨集合点对之间的最小割。

这个很好理解,因为当前找到的最小割将s集和t集分开了,显然对于任意一组跨集合的点对而言,当前最小割都是一个可能的最小割。

然后我们再递归处理s集和t集(重复以上步骤)。

每次找到最小割后就更新跨集合点对。

本质上是分治吧。

之前看有些地方提到了最小割树,这里放个链接(这是我找到的写的最全的一篇了)

Gomory-Hu tree 最小割树

下面放代码吧,个人觉得看代码会更好理解,尤其是对分治不熟悉的人(比如我)

不同的最小割(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]不同的最小割 分治求最小割的更多相关文章

  1. (模板)hdoj1007(分治求平面最小点对)

    题目链接:https://vjudge.net/problem/HDU-1007 题意:给定n个点,求平面距离最小点对的距离除2. 思路:分治求最小点对,对区间[l,r]递归求[l,mid]和[mid ...

  2. 【LuoguP3329&4123】[ZJOI2011]最小割&[CQOI2016]不同的最小割

    链接1 链接2 题意简述 第一个题 : 问图中有多少不同的最小割数值 第二个题 : \(q\) 次询问图中多少对点对之间的最小割小于 \(x\) . Sol 两个都是模板题就放一起了. 求完最小割树直 ...

  3. 不同的最小割(cqoi2016,bzoj4519)(最小割树)

    学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成 两个部分,如果结点\(s,t\)不在同一个部分中,则称这个划分是关于\(s,t\)的割.对于带权图来说,将 所有顶 ...

  4. 【bzoj2229】[Zjoi2011]最小割 分治+网络流最小割

    题目描述 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分 ...

  5. Uvaoj 11248 Frequency Hopping(Dinic求最小割)

    题意:1到n节点(节点之间有一定的容量),需要流过C的流量,问是否可以?如果可以输出possible, 否则如果可以扩大任意一条边的容量 可以达到目的,那么输出possible option:接着输出 ...

  6. hiho 第116周,最大流最小割定理,求最小割集S,T

    小Hi:在上一周的Hiho一下中我们初步讲解了网络流的概念以及常规解法,小Ho你还记得内容么? 小Ho:我记得!网络流就是给定了一张图G=(V,E),以及源点s和汇点t.每一条边e(u,v)具有容量c ...

  7. poj 3469 Dual Core CPU【求最小割容量】

    Dual Core CPU Time Limit: 15000MS   Memory Limit: 131072K Total Submissions: 21453   Accepted: 9297 ...

  8. BZOJ_1001_狼抓兔子_(平面图求最小割+对偶图求最短路)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1001 1001: [BeiJing2006]狼抓兔子 Time Limit: 15 Sec   ...

  9. HDU - 3035 War(对偶图求最小割+最短路)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3035 题意 给个图,求把s和t分开的最小割. 分析 实际顶点和边非常多,不能用最大流来求解.这道题要用 ...

随机推荐

  1. nginx下pagespeed使用详解

    目录 1.简介 2.安装 2.1脚本安装 查看该脚本的如何使用 使用脚本自动安装 替换以前的nginx 2.2 手动安装 先安装基本依赖 构建pagespeed 重新编译安装nginx 3.配置 3. ...

  2. golang 仿python pack/unpack

    写得不完善也不完美 尤其是高低位转换那(go和c 二进制高地位相反 需要转换,还有go int转[]byte长度是4位),希望牛人看后指导一下 项目需要通过socket调取 客户端是go ,服务器端是 ...

  3. 如何实现最佳的跨平台游戏体验?Unity成亮解密实时渲染

    7月31日,2018云创大会游戏论坛在杭州国际博览中心103B圆满举行.本场游戏论坛聚焦探讨了可能对游戏行业发展有重大推动的新技术.新实践,如AR.区块链.安全.大数据等. Unity大中华区技术经理 ...

  4. 使用git bash编译安装sysbench时遇到的坑

      Preface       When I was compiling the sysbench just now,I encountered some failures in the preced ...

  5. hdu2037今年暑假不AC(贪心,活动安排问题)

    今年暑假不AC Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 65536/32768K (Java/Other) Total Submi ...

  6. Unity编辑器 - 资源批处理工具基类

    Unity编辑器 - 资源批处理工具基类 经常要对资源进行批处理,很多时候都是一次性的需求,于是弄个通用脚本. 工具是个弹出面板,处理过程有进度条,如下: 如图,子类只需要重写几个方法: using ...

  7. Resharp使用简记

    一图流: 拾贝: .字符串引号中直接回车键自动添加连接字符串 Ctrl+B:转到定义 Ctr+F11:展示类结构 Ctr+Alt+j:包围代码块 Ctr+\:注释和取消注释 Alt+Ins:重构 Ct ...

  8. EF中如何为表添加新的字段和映射

    首先先了解一下ef生成的模型edmx的代码,传送门:http://www.cnblogs.com/yushengbo/p/4807715.html 一.添加新的字段 例子就用我现在项目的这个吧,首先在 ...

  9. 解析范式(1NF-4NF)

    亲爱的盆友们~又是新的一年,你,准备好新的学习计划了吗~?是读书100本,还是考上5个证?嘛~不管怎么说,角落里那一堆蒙尘的计划表好像在昭示着这仍然是一个充满朝气又艰难的9102年呢!总之,先把#技本 ...

  10. kosaraju求强连通分量

    在了解kosaraju算法之前我们先了解一下什么是强连通分量,在有向图中如果两个定点vi,ui存在一条路劲从vi到达ui且也存在一条路劲从ui到达vi那么由ui和vi这两个点构成的图成为强连通图,简洁 ...