2521: [Shoi2010]最小生成树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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
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
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 ",Dinic()); return 0; }