题解【[POI2010]MOS-Bridges】
传送门。
$\texttt{Description}$
每条边正向和反向有两个权值,求最大边权尽量小的欧拉回路。
$1\le n\le 10^3$,$1\le m\le 2\times 10^3$。
$\texttt{Solution}$
前置芝士。
一道题,没有烟,一道毒瘤调一天。
聪明的你学会了前置芝士,知道了无解的一个充分条件是,每个点的入度都为偶数。
看到最大边权尽量小,条件反射出二分答案。
我们就不妨二分最大的边权 $k$,那么原图中边权大于 $k$ 的边都不用管。
看到 $n,m$ 稍大,考虑构建一个二分图:左边是边,右边是点。
我们寄 $in_i$ 表示 $i$ 点的入度,显然 $in_i$ 是偶数。
则到达这个点边的数量一定等于从这个点出发的边的数量。
所以从 $i$ 点流出的流量,即流向 $t$ 的流量,一定是 $\frac{in_i}{2}$,有一半是流入的。
根据欧拉回路的定义,每一条边只能匹配一次,所以可以把 $s$ 向每一条边连一条容量为 $1$ 的边。
边和点的连边也很简单了,我们对于原图的一条边 $(u,v,w_1,w_2)$,如果 $w_1\le k$,则 $u$ 可以到达 $v$,也就是说这条边可以流到 $v$ 这里。则这条边向 $v$ 连一条容量为 $1$ 的边。$w_2$ 的情况也同理。
跑一遍最大流,只需要判断最大流是否满流即可。也就是 $\frac{1}{2}\sum in_i$。
对于输出方案,我们枚举每一条边 $i$,如果 $i\to v_i$ 的残余流量为 $0$,说明这条边是选择的,也就是 $u_i\to v_i$ 是被选择的,直接建一张新图,把这条边加进去就行了;$i\to u_i$ 残余流量为 $0$ 的情况也一样。
最后只需要在新图中找一条欧拉路径,模板题了吧。。。。。
核心代码:
inline void remake() { Clear(head),Clear(edge); tot=1; return; } int in[MAXN]; inline bool check(int k) { remake(); int all(0); rep(i,1,n) add_edge(i+m,t,in[i]>>1),all+=(in[i]>>1); rep(i,1,m) { add_edge(s,i,1); if(a[i].w1<=k) add_edge(i,a[i].y+m,1); if(a[i].w2<=k) add_edge(i,a[i].x+m,1); } int ans(0); while(bfs()) ans+=dfs(s,INF); return ans==all; } int out[MAXN],p; bool vis[MAXN]; inline void Find(int u,int lst) { while(head2[u]) { E e=edge2[head2[u]]; head2[u]=e.nxt; Find(e.to,e.flow); } if(lst) out[++p]=lst; } int main() { n=read(),m=read(); s=0,t=n+m+1; rep(i,1,m) { a[i].x=read(),a[i].y=read(),a[i].w1=read(),a[i].w2=read(); ++in[a[i].x],++in[a[i].y]; } rep(i,1,n) if(in[i]&1) { puts("NIE"); return 0; } int l=1,r=1000,res(-1); while(l<=r) { int mid=(l+r)>>1; if(check(mid)) r=mid-1,res=mid; else l=mid+1; } if(res==-1) { puts("NIE"); return 0; } printf("%d\n",res); check(res); rep(i,1,m) gra(j,i) { E e=edge[j]; if(e.to!=s&&(!e.flow)) { if(e.to-m==a[i].y) add_edge2(a[i].x,a[i].y,i); else add_edge2(a[i].y,a[i].x,i); } } Find(1,0); rep(i,1,p) printf("%d ",out[p-i+1]); return 0; }
$$\texttt{The End.by UF}$$