LoibreOJ 2042. 「CQOI2016」不同的最小割 最小割树 Gomory-Hu tree

本文介绍了一道图论问题——CQOI2016的不同最小割,通过Gomory-Hu树算法解决任意两点间的最小割问题。详细解释了Gomory-Hu树的构建过程,并提供了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2042. 「CQOI2016」不同的最小割

内存限制:256 MiB时间限制:1000 ms标准输入输出
题目类型:传统评测方式:文本比较
上传者: 匿名
题目描述

学过图论的同学都知道最小割的概念:对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点 s,ts, ts,t 不在同一个部分中,则称这个划分是关于 s,ts, ts,t 的割。对于带权图来说,将所有顶点处在不同部分的边的权值相加所得到的值定义为这个割的容量,而 s,ts, ts,t 的最小割指的是在关于 s,ts, ts,t 的割中容量最小的割。

而对冲刺 NOI 竞赛的选手而言,求带权图中两点的最小割已经不是什么难事了。我们可以把视野放宽,考虑有 NNN 个点的无向连通图中所有点对的最小割的容量,共能得到 N(N1)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 #NNNMMM
1252525150150150
2505050500500500
3100100100100010001000
4150150150150015001500
5200200200200020002000
6300300300300030003000
7400400400400040004000
8500500500500050005000
9700700700700070007000
10850850850850085008500

对于所有测试点,w≤100000w \leq 100000w100000。

 

题目链接: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+100,maxm=1e5+100,inf=0x3f3f3f3f,mod=1e9+7;
const ll INF=1e18+7;
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=0; i<=n+10; i++) G[i].clear();
    es.clear();
}
void addedge(int from,int to,int cap)
{
    es.push_back((edge)
    {
        from,to,cap,0
    });
    es.push_back((edge)
    {
        to,from,0,0
    });
    int x=es.size();
    G[from].push_back(x-2);
    G[to].push_back(x-1);
}
bool BFS(int s,int t)
{
    memset(vis,0,sizeof(vis));
    queue <int> Q;
    vis[s]=1;
    dist[s]=0;
    Q.push(s);
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        for (int i=0; i<G[u].size(); i++)
        {
            edge &e=es[G[u][i]];
            if (!vis[e.to]&&e.cap>e.flow)
            {
                vis[e.to]=1;
                dist[e.to]=dist[u]+1;
                Q.push(e.to);
            }
        }
    }
    return vis[t];
}
int DFS(int u,int t,int f)
{
    if(u==t||f==0) return f;
    int flow=0,d;
    for(int &i=iter[u]; i<G[u].size(); i++)
    {
        edge &e=es[G[u][i]];
        if(dist[u]+1==dist[e.to]&&(d=DFS(e.to,t,min(f,e.cap-e.flow)))>0)
        {
            e.flow+=d;
            es[G[u][i]^1].flow-=d;
            flow+=d;
            f-=d;
            if (f==0) break;
        }
    }
    return flow;
}
int Maxflow(int s,int t)
{
    for(int i=0; i<es.size(); i++) es[i].flow=0;
    int flow=0;
    while(BFS(s,t))
    {
        memset(iter,0,sizeof(iter));
        int d=0;
        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=1; 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=2; i<=n; i++) pa[i]=1;
    for(int u=2; u<=n; u++)
    {
        int v=pa[u];
        int flow=Maxflow(u,v);
        //cout<<u<<" "<<v<<" "<<flow<<endl;
        ans.insert(flow);
        for(int j=u+1; j<=n; j++)
            if(pa[j]==v&&vis[j]) pa[j]=u;
    }
    printf("%d\n",ans.size());
    return 0;
}
最小割树

 

转载于:https://www.cnblogs.com/GeekZRF/p/7325055.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值