BZOJ2561: 最小生成树
Description
给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1到N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?
Input
第一行包含用空格隔开的两个整数,分别为N和M;
接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。
最后一行包含用空格隔开的三个整数,分别为u,v,和 L;
数据保证图中没有自环。
接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。
最后一行包含用空格隔开的三个整数,分别为u,v,和 L;
数据保证图中没有自环。
Output
输出一行一个整数表示最少需要删掉的边的数量。
Sample Input
3 2
3 2 1
1 2 3
1 2 2
3 2 1
1 2 3
1 2 2
Sample Output
1
HINT
对于20%的数据满足N ≤ 10,M ≤ 20,L ≤ 20;
对于50%的数据满足N ≤ 300,M ≤ 3000,L ≤ 200;
对于100%的数据满足N ≤ 20000,M ≤ 200000,L ≤ 20000。
我们考虑什么时候特殊边(u,v,l)不可能出现在最小生成树上。
由Kruskal不难发现,当有一条从u到v的路径满足路径上每一条边都<l则不可行。
那么这其实就是一个最小割模型。
类似再算一遍最大生成树就行了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i!=-1;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=Getchar(); for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1; for(;isdigit(c);c=Getchar()) x=x*10+c-'0'; return x*f; } const int maxn=20010; const int maxm=400010; const int inf=1e9; struct Dinic { int n,m,s,t,clo,cur[maxn],vis[maxn],d[maxn]; int first[maxn],next[maxm]; struct Edge {int from,to,flow;}edges[maxm]; void init(int n) { this->n=n;m=0; memset(first,-1,sizeof(first)); } void AddEdge(int u,int v,int w) { edges[m]=(Edge){u,v,w};next[m]=first[u];first[u]=m++; edges[m]=(Edge){v,u,w};next[m]=first[v];first[v]=m++; } int Q[maxn]; int bfs() { int l=1,r=1;Q[l]=s;vis[s]=++clo; while(l<=r) { int x=Q[l++];cur[x]=first[x]; ren { Edge& e=edges[i]; if(e.flow&&vis[e.to]!=clo) { vis[e.to]=clo; d[e.to]=d[x]+1; Q[++r]=e.to; } } } return vis[t]==clo; } int dfs(int x,int a) { if(x==t||!a) return a; int flow=0,f; for(int& i=cur[x];i!=-1;i=next[i]) { Edge& e=edges[i]; if(d[e.to]==d[x]+1&&(f=dfs(e.to,min(a,e.flow)))) { e.flow-=f;edges[i^1].flow+=f; flow+=f;a-=f;if(!a) break; } } return flow; } int solve(int s,int t) { this->s=s;this->t=t;int flow=0; while(bfs()) flow+=dfs(s,inf); return flow; } }sol; int u[maxm],v[maxm],w[maxm]; int main() { int n=read(),m=read(); rep(i,1,m+1) u[i]=read(),v[i]=read(),w[i]=read(); sol.init(n); rep(i,1,m) if(w[i]>w[m+1]) sol.AddEdge(u[i],v[i],1); int ans=sol.solve(u[m+1],v[m+1]); sol.init(n); rep(i,1,m) if(w[i]<w[m+1]) sol.AddEdge(u[i],v[i],1); printf("%d\n",ans+sol.solve(u[m+1],v[m+1])); return 0; }