【BZOJ-2521】最小生成树 最小割

2521: [Shoi2010]最小生成树

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 415  Solved: 242
[Submit][Status][Discuss]

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

Source

day2

Solution

水题

首先考虑,选择一条边不变,其余边权-1;显然就相当于,选择一条边权+1,其余边不变

要求选定边id一定在最小生成树上,考虑一下求最小生成树的过程Kruskal

显然对这条边有影响的边是初始边权比他小的边,若id边一定加入到最小生成树中,也就是说,边权<=val[id]的边中,不存在能使x[id],y[id]的联通的边

那么对于一条边x,使他不对id产生影响的最小代价是val[id]+1-val[x](使他边权变成恰大于id边权)

那么显然最小化代价,用最小割处理即可

把初始边权小于等于id的边相连,约束为val[id]+1-val[x],然后跑x[id]到y[id]的最小割即可

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
void Freopen () {freopen("build.in","r",stdin); freopen("build.out","w",stdout);}
void Fclose() {fclose(stdin); fclose(stdout);}
#define MAXM 1000010
#define MAXN 1010
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
int N,M,id;
struct RoadNode{int u,v,w,id;}road[MAXM];
struct EdgeNode{int next,to,cap;}edge[MAXM<<1];
int head[MAXN],cnt=1;
void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].cap=w;}
void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,0);}
int h[MAXN],cur[MAXN],S,T;
bool BFS()
{
    queue<int>q;
    for (int i=S; i<=T; i++) h[i]=-1;
    q.push(S); h[S]=0;
    while (!q.empty())
        {
            int now=q.front(); q.pop();
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].cap && h[edge[i].to]==-1)
                    h[edge[i].to]=h[now]+1,q.push(edge[i].to);
        }
    return h[T]!=-1;
}
int DFS(int loc,int low)
{
    if (loc==T) return low;
    int used=0,w;
    for (int i=cur[loc]; i; i=edge[i].next)
        if (edge[i].cap && h[edge[i].to]==h[loc]+1)
            {
                w=DFS(edge[i].to,min(edge[i].cap,low-used));
                edge[i].cap-=w,edge[i^1].cap+=w; used+=w;
                if (edge[i].cap) cur[loc]=i;
                if (used==low) return low;
            }
    if (!used) h[loc]=-1;
    return used;
}
#define INF 0x7fffffff
int Dinic()
{
    int re=0;
    while (BFS())
        {
            for (int i=S; i<=T; i++) cur[i]=head[i];
            re+=DFS(S,INF);
        }
    return re;
}
int D;
void BuildGraph()
{
    S=0,T=N+1;
    for (int i=1; i<=M; i++)
        if (road[i].w<=D)
            {
                if (road[i].id!=id) 
                    InsertEdge(road[i].u,road[i].v,D+1-road[i].w),
                    InsertEdge(road[i].v,road[i].u,D+1-road[i].w);
                 else InsertEdge(S,road[i].u,INF),InsertEdge(road[i].v,T,INF);
            }
        else break;
}
bool cmp(RoadNode A,RoadNode B) {return A.w<B.w;}
int main()
{
    N=read(),M=read(),id=read();
    for (int i=1; i<=M; i++)
        road[i].u=read(),road[i].v=read(),road[i].w=read(),road[i].id=i;
    D=road[id].w;
    sort(road+1,road+M+1,cmp);
    BuildGraph();
    printf("%d\n",Dinic());
    return 0;
}

 

posted @ 2016-08-14 17:27  DaD3zZ  阅读(401)  评论(0编辑  收藏  举报