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

Solution

首先题目的操作其实可以看成给一条边权值加一……

首先对于权值比$lab$大的边,我们肯定是不需要管的,因为按照$kruskal$的过程,他们一定在$lab$的后面考虑。

而对于权值比$lab$小的,我们可以通过给他们不停加一使得权值超过$lab$从而靠后考虑。

可以发现,当$(u[lab],v[lab])$这条边会被算到最小生成树里面,只有在权值小于等于它的边加完后,$u[lab]$和$v[lab]$不在一个连通块内。我们把权值小于等于$l[lab]$的图建出来,现在问题变成,你可以用$l[lab]-l[i]+1$的代价砍掉一些边使得$u[lab]$和$v[lab]$不连通,最小割就好了。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #include<algorithm>
 6 #define N (1009)
 7 #define INF (0x7f7f7f7f)
 8 using namespace std;
 9 
10 struct Edge{int to,next,flow;}edge[N<<1];
11 int n,m,lab,u[N],v[N],l[N],Depth[N];
12 int head[N],num_edge;
13 queue<int>q;
14 
15 inline int read()
16 {
17     int x=0,w=1; char c=getchar();
18     while (c<'0' || c>'9') {if (c=='-') w=-1; c=getchar();}
19     while (c>='0' && c<='9') x=x*10+c-'0', c=getchar();
20     return x*w;
21 }
22 
23 void add(int u,int v,int l)
24 {
25     edge[++num_edge].to=v;
26     edge[num_edge].next=head[u];
27     edge[num_edge].flow=l;
28     head[u]=num_edge;
29 }
30 
31 int DFS(int x,int low,int t)
32 {
33     if (x==t || !low) return low;
34     int f=0;
35     for (int i=head[x]; i; i=edge[i].next)
36         if (Depth[edge[i].to]==Depth[x]+1)
37         {
38             int Min=DFS(edge[i].to,min(low,edge[i].flow),t);
39             edge[i].flow-=Min;
40             edge[((i-1)^1)+1].flow+=Min;
41             f+=Min; low-=Min;
42             if (!low) break;
43         }
44     if (!f) Depth[x]=-1;
45     return f;
46 }
47 
48 bool BFS(int s,int t)
49 {
50     memset(Depth,0,sizeof(Depth));
51     Depth[s]=1;
52     q.push(s);
53     while (!q.empty())
54     {
55         int x=q.front(); q.pop();
56         for (int i=head[x]; i; i=edge[i].next)
57             if (!Depth[edge[i].to] && edge[i].flow)
58             {
59                 Depth[edge[i].to]=Depth[x]+1;
60                 q.push(edge[i].to);
61             }
62     }
63     return Depth[t];
64 }
65 
66 int Dinic(int s,int t)
67 {
68     int ans=0;
69     while (BFS(s,t)) ans+=DFS(s,INF,t);
70     return ans;
71 }
72 
73 int main()
74 {
75     n=read(); m=read(); lab=read();
76     for (int i=1; i<=m; ++i) u[i]=read(),v[i]=read(),l[i]=read();
77     for (int i=1; i<=m; ++i)
78         if (i!=lab && l[i]<=l[lab])
79         {
80             add(u[i],v[i],l[lab]-l[i]+1);
81             add(v[i],u[i],l[lab]-l[i]+1);
82         }
83     printf("%d\n",Dinic(u[lab],v[lab]));
84 }

转载于:https://www.cnblogs.com/refun/p/10523201.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值