[BZOJ2521] [Shoi2010]最小生成树

题目描述

Description

Secsa最近对最小生成树问题特别感兴趣。他已经知道如果要去求出一个n个点、m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法。另外,他还知道,某一个图可能有多种不同的最小生成树。例如,下面图 3中所示的都是图 2中的无向图的最小生成树:

当然啦,这些都不是今天需要你解决的问题。Secsa想知道对于某一条无向图中的边AB,至少需要多少代价可以保证AB边在这个无向图的最小生成树中。为了使得AB边一定在最小生成树中,你可以对这个无向图进行操作,一次单独的操作是指:先选择一条图中的边 P1P2,再把图中除了这条边以外的边,每一条的权值都减少1。如图 4所示就是一次这样的操作:

Input

输入文件的第一行有3个正整数n、m、Lab分别表示无向图中的点数、边数、必须要在最小生成树中出现的AB边的标号。
接下来m行依次描述标号为1,2,3…m的无向边,每行描述一条边。每个描述包含3个整数x、y、d,表示这条边连接着标号为x、y的点,且这条边的权值为d。
输入文件保证1<=x,y<=N,x不等于y,且输入数据保证这个无向图一定是一个连通图。

Output

输出文件只有一行,这行只有一个整数,即,使得标号为Lab边一定出现最小生成树中的最少操作次数。

Sample Input

4 6 1
1 2 2
1 3 2
1 4 3
2 3 2
2 4 4
3 4 5

Sample Output

1

HINT

第1个样例就是问题描述中的例子。
1<=n<=500,1<=M<=800,1<=D<10^6

题目分析

每次操作相当于将选中的这条边的边权+1
如果我们选中的这条边出现在了最小生成树里 那么 这条边必须是能够连接两个端点的 边权最小的边
那么最小割就好了 图边权设为这条边的边权和选定边权的差 S,T 设为选定的边的两个端点

#include <cstdio>
#include <cstring>
#include <set>
#include <map>
#include <vector>
#include <cmath>
#include <queue>
#include <algorithm>
using namespace std;
int n,m,num;
struct your
{
    int x,y,c;
}a[2000];
int tot=1;
int head[600],net[4000],to[4000],val[4000];
void add(int x,int y,int c)
{
    net[++tot]=head[x],head[x]=tot,to[tot]=y,val[tot]=c;
    net[++tot]=head[y],head[y]=tot,to[tot]=x,val[tot]=0;
}
int s,t;
int dis[600];
int bfs()
{
    memset(dis,0,sizeof dis);
    queue<int>q;
    q.push(s),dis[s]=1;
    while(q.size())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=net[i])
        {
            if(val[i]&&!dis[to[i]])
            {
                dis[to[i]]=dis[x]+1,q.push(to[i]);
                if(to[i]==t) return 1;
            }
        }
    }
    return 0;
}
int dinic(int x,int flow)
{
    int temp=flow;
    if(x==t) return flow;
    for(int i=head[x];i;i=net[i])
        if(val[i]&&dis[to[i]]==dis[x]+1)
        {
            int tmp=dinic(to[i],min(temp,val[i]));
            if(!tmp) dis[to[i]]=0;
            temp-=tmp,val[i]-=tmp,val[i^1]+=tmp;
            if(!temp) break;
        }   
    return flow-temp;
}
int main()
{   
    scanf("%d%d%d",&n,&m,&num);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
    for(int i=1;i<=m;i++)
    {
        if(a[i].c>a[num].c||i==num) continue;
        add(a[i].x,a[i].y,a[num].c-a[i].c+1),add(a[i].y,a[i].x,a[num].c-a[i].c+1);
    }
    s=a[num].x,t=a[num].y;
    int ans=0;
    while(bfs()) ans+=dinic(s,1<<30);
    printf("%d",ans);
    return 0;
}

关于“[BZOJ2521] [Shoi2010]最小生成树”我的1个想法

发表评论

邮箱地址不会被公开。 必填项已用*标注