最小生成树 bzoj-2561
题目大意;题目链接。
注释:略。
想法:
我们发现:
如果一条权值为$L$的边想加入到最小生成树上的话,需要满足一下条件。
就是求出原图的最小生成树之后,这个边当做非树边的情况下覆盖的边的最小值不可以比$L$小。
如此,我们级就可以通过网络流来求了。
对于每一条比$L$小的边,我们连双向边,求当前边$u,v$的最小割即可。
如果是最大生成树的话同理,我们只需要把所有比当前加入的边的权值小的边在网络流中连无向边,然后跑最小割即可。
Code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 200100
#define M 2000100
using namespace std;
int to[M<<1],head[N],nxt[M<<1],val[M<<1],dis[N],tot=1,S,T;
queue<int>q;
struct Node {int x,y,w;}e[M]; inline bool cmp_w(const Node &a,const Node &b) {return a.w<b.w;}
char *p1,*p2,buf[100000];
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {int x=0,f=1; char c=nc(); while(c<48) {if(c=='-') f=-1; c=nc();} while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x*f;}
inline void add(int x,int y,int z)
{
to[++tot]=y; val[tot]=z; nxt[tot]=head[x]; head[x]=tot;
to[++tot]=x; val[tot]=0; nxt[tot]=head[y]; head[y]=tot;
}
bool bfs()
{
memset(dis,-1,sizeof dis); while(!q.empty()) q.pop();
dis[S]=0; q.push(S); while(!q.empty())
{
int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]==-1&&val[i]>0)
{
dis[to[i]]=dis[x]+1; q.push(to[i]);
if(to[i]==T) return true;
}
}
return false;
}
int dinic(int x,int fl)
{
int tmp=fl; if(x==T) return fl; for(int i=head[x];i;i=nxt[i]) if(dis[to[i]]==dis[x]+1&&val[i]>0)
{
int mdl=dinic(to[i],min(val[i],tmp));
if(!mdl) dis[to[i]]=-1;
val[i]-=mdl; tmp-=mdl; val[i^1]+=mdl;
if(!tmp) break;
}
return fl-tmp;
}
int main()
{
int n=rd(),m=rd(); for(int i=1;i<=m;i++) e[i].x=rd(),e[i].y=rd(),e[i].w=rd();
int u=rd(),v=rd(),L=rd(); S=u,T=v;
int ans=0;
for(int i=1;i<=m;i++) if(e[i].w<L) add(e[i].x,e[i].y,1),add(e[i].y,e[i].x,1);
while(bfs()) ans+=dinic(u,1<30);
memset(head,0,sizeof head); tot=1;
for(int i=1;i<=m;i++) if(e[i].w>L) add(e[i].x,e[i].y,1),add(e[i].y,e[i].x,1);
while(bfs()) ans+=dinic(u,1<<30);
cout << ans << endl ;
return 0;
}
小结:好玩吧。