2042. 「CQOI2016」不同的最小割
题目描述
学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点 s,ts, ts,t 不在同一个部分中,则称这个划分是关于 s,ts, ts,t 的割。对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而 s,ts, ts,t 的最小割指的是在关于 s,ts, ts,t 的割中容量最小的割。
而对冲刺 NOI 竞赛的选手而言,求带权图中两点的最小割已经不是什么难事了。我们可以把视野放宽,考虑有 NNN 个点的无向连通图中所有点对的最小割的容量,共能得到 N(N−1)2 个数值。这些数值中互不相同的有多少个呢?这似乎是个有趣的问题。
输入格式
输入文件第一行包含两个数 N,MN, MN,M,表示点数和边数。
接下来 MMM 行,每行三个数 u,v,wu, v, wu,v,w,表示点 uuu 和点 vvv(从 111 开始标号)之间有一条权值是 www 的边。
输出格式
输出文件第一行为一个整数,表示不同的最小割容量的个数。
样例
样例输入
4 4
1 2 3
1 3 6
2 4 5
3 4 4
样例输出
3
数据范围与提示
Case # | NNN | MMM |
---|---|---|
1 | 252525 | 150150150 |
2 | 505050 | 500500500 |
3 | 100100100 | 100010001000 |
4 | 150150150 | 150015001500 |
5 | 200200200 | 200020002000 |
6 | 300300300 | 300030003000 |
7 | 400400400 | 400040004000 |
8 | 500500500 | 500050005000 |
9 | 700700700 | 700070007000 |
10 | 850850850 | 850085008500 |
对于所有测试点,w≤100000w \leq 100000w≤100000。
题目链接:https://loj.ac/problem/2042
题意:中文题目,意思很明显。就是求一个图以任意顶点作为s和t求有几种最小割。
思路:Gomory-Hu tree,表示图中所有s - t对的最小s - t切割的加权树。
Gomory-Hu tree建立的过程:
首先以1为根建立一颗菊花树
然后对于2到n每个节点:跑它(S)和它父亲(T)的最小割(初始时,每个点的父亲节点均是1)
得出来的最小割即为树上该条边的权值
然后找到比该节点编号大的节点,如果它的父亲为T且它在S集中(dinic后还能被增广到的点属于S集,即vis数组),那么把它的父亲置为S。
复杂度就是最小割*n
然后树上两点间路径的瓶颈即为两点间最小割。
(其实我也不知道为什么是这样)
代码:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
#define PI acos(-1.0)
const int maxn=2e3+,maxm=1e5+,inf=0x3f3f3f3f,mod=1e9+;
const ll INF=1e18+;
struct edge
{
int from,to,cap,flow;
};
vector<edge>es;
vector<int>G[maxn];
bool vis[maxn];
int dist[maxn];
int iter[maxn];
void init(int n)
{
for(int i=; i<=n+; i++) G[i].clear();
es.clear();
}
void addedge(int from,int to,int cap)
{
es.push_back((edge)
{
from,to,cap,
});
es.push_back((edge)
{
to,from,,
});
int x=es.size();
G[from].push_back(x-);
G[to].push_back(x-);
}
bool BFS(int s,int t)
{
memset(vis,,sizeof(vis));
queue <int> Q;
vis[s]=;
dist[s]=;
Q.push(s);
while(!Q.empty())
{
int u=Q.front();
Q.pop();
for (int i=; i<G[u].size(); i++)
{
edge &e=es[G[u][i]];
if (!vis[e.to]&&e.cap>e.flow)
{
vis[e.to]=;
dist[e.to]=dist[u]+;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int u,int t,int f)
{
if(u==t||f==) return f;
int flow=,d;
for(int &i=iter[u]; i<G[u].size(); i++)
{
edge &e=es[G[u][i]];
if(dist[u]+==dist[e.to]&&(d=DFS(e.to,t,min(f,e.cap-e.flow)))>)
{
e.flow+=d;
es[G[u][i]^].flow-=d;
flow+=d;
f-=d;
if (f==) break;
}
}
return flow;
}
int Maxflow(int s,int t)
{
for(int i=; i<es.size(); i++) es[i].flow=;
int flow=;
while(BFS(s,t))
{
memset(iter,,sizeof(iter));
int d=;
while(d=DFS(s,t,inf)) flow+=d;
}
return flow;
}
int pa[maxn];
set<int>ans;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
for (int i=; i<=m; i++)
{
int u,v,r;
scanf("%d %d %d",&u,&v,&r);
addedge(u,v,r);
addedge(v,u,r);
}
for(int i=; i<=n; i++) pa[i]=;
for(int u=; u<=n; u++)
{
int v=pa[u];
int flow=Maxflow(u,v);
//cout<<u<<" "<<v<<" "<<flow<<endl;
ans.insert(flow);
for(int j=u+; j<=n; j++)
if(pa[j]==v&&vis[j]) pa[j]=u;
}
printf("%d\n",ans.size());
return ;
}
最小割树