【LOJ#2460】桥 Bridges
题目
题目链接:https://loj.ac/problem/2460
在一个无向图中找一条最大边权最小的欧拉回路。若没有则输出 NIE
。
思路
这题思路还是挺妙的,码量也不算小,感觉是一道好题(吗?)。
因为如果最大值为 \(a\) 的时候没办法形成欧拉回路,显然最大值小于 \(a\) 的时候也没法形成欧拉回路。所以果断二分。
接下来这张无向图就被拆成了既有双向边又有单向边的图。那么我们需要通过选择双向边的方向使这张图变成一张有向图且含有欧拉回路。
欧拉回路的判定为:不存在任何一个点的出度和入度不等。
我们把每一条双向边任意假定一个方向。然后求出每个点的 \(deg\) 表示入度与出度的差值。显然我们需要改变双向边的方向使得所有点的 \(deg=0\)。
考虑网络流。若一 \(deg[x]\geq 0\),则连边 \((S,x,\frac{deg[x]}{2})\),若 \(deg[x]<0\),则连边 \((x,T,\frac{-deg[x]}{2})\)。
如果一条双向边我们假定的是 \(a\to b\),那么我们如果要把这条边反过来,那么就会造成 \(deg[a]-2,deg[b]+2\)。所以连边 \((a,b,1)\)。
显然,上述模型的充分条件是
- 不存在边满足两个权值都大于 \(mid\)。
- 不存在边的 \(deg\) 是奇数。
所以如果上述两个条件有一个不满足直接返回即可。
当 \(∀i\) 满足 \(deg[i]=0\) 时,显然流量是 \(\frac{\sum^{n}_{i=1}\operatorname{abs}deg[i]}{2}\)。判断是否满流即可。
当我们求出答案时,把答案所对应的网络图拿出来,对于双向边,如果有流量流过去,相当于这条边要反向。
那么我们就得到了一张有向图且这张图一定有欧拉回路,并且每条边的权值都不超过答案。那么直接 dfs 找出欧拉回路即可。
时间复杂度 \(O(n^2m\log a_i)\)。
代码
感觉最近这种调了很久的题代码就丑的一匹。。。
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include <queue>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=2010,M=100010,Inf=1e9+114514;
int n,m,S,T,tot,top,L,R,Mid,maxflow,deg[N],last[N],head[N],st[N],out[N];
bool vis[M],WYCtql;
vector<int> r[N];
struct edge1
{
int from,to,a,b;
}e[M];
void add(int from,int to,int a,int b)
{
e[++tot].to=to;
e[tot].from=from;
e[tot].a=a;
e[tot].b=b;
}
struct edge2
{
int next,to,flow;
}ans[M];
struct edge3
{
int next,to;
}ee[M];
void add2(int from,int to)
{
out[from]++;
ee[++tot].to=to;
ee[tot].next=head[from];
head[from]=tot;
}
struct Dinic
{
int tot,head[N],cur[N],dep[N];
edge2 e[M];
void clr()
{
memset(head,-1,sizeof(head));
tot=1;
}
void add(int from,int to,int flow)
{
e[++tot].to=to;
e[tot].flow=flow;
e[tot].next=head[from];
head[from]=tot;
swap(from,to);
e[++tot].to=to;
e[tot].flow=0;
e[tot].next=head[from];
head[from]=tot;
}
bool bfs()
{
memcpy(cur,head,sizeof(cur));
memset(dep,0x3f3f3f3f,sizeof(dep));
queue<int> q; q.push(S);
dep[S]=0;
while (q.size())
{
int u=q.front();
q.pop();
for (int i=head[u];~i;i=e[i].next)
{
int v=e[i].to;
if (e[i].flow && dep[v]>dep[u]+1)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[T]<Inf;
}
int dfs(int x,int flow)
{
int ret=0,used=0;
if (x==T)
{
maxflow+=flow;
return flow;
}
for (int i=cur[x];~i;i=e[i].next)
{
int v=e[i].to;
cur[x]=i;
if (e[i].flow && dep[x]==dep[v]-1)
{
ret=dfs(v,min(flow-used,e[i].flow));
if (ret)
{
used+=ret;
e[i].flow-=ret; e[i^1].flow+=ret;
if (used==flow) break;
}
}
}
return used;
}
void dinic()
{
maxflow=0;
while (bfs()) dfs(S,Inf);
}
}dinic;
bool check(int mid)
{
memset(deg,0,sizeof(deg));
for (int i=1;i<=m;i++)
{
if (min(e[i].a,e[i].b)>mid) return 0;
if (e[i].a<=mid)
deg[e[i].from]++,deg[e[i].to]--;
else
deg[e[i].to]++,deg[e[i].from]--;
}
dinic.clr();
for (int i=1;i<=m;i++)
if (e[i].a<=mid && e[i].b<=mid)
dinic.add(e[i].from,e[i].to,1);
int sum=0;
for (int i=1;i<=n;i++)
{
if (abs(deg[i])&1) return 0;
deg[i]/=2; sum+=abs(deg[i]);
if (deg[i]>=0) dinic.add(S,i,deg[i]);
else dinic.add(i,T,-deg[i]);
}
dinic.dinic();
return maxflow==sum/2;
}
void print(int x)
{
for (int i=head[x];~i;i=ee[i].next)
{
int k=i;
if (!vis[k]) {
vis[k]=1;
print(ee[k].to);
st[++top]=k;
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
srand(n+m+23333);
L=Inf; S=N-1; T=N-2;
for (int i=1,x,y,a,b;i<=m;i++)
{
scanf("%d%d%d%d",&x,&y,&a,&b);
add(x,y,a,b);
L=min(L,min(a,b)); R=max(R,max(a,b));
}
bool flag=0;
while (L<=R)
{
Mid=(L+R)>>1;
if (check(Mid))
{
R=Mid-1; flag=1;
memcpy(ans,dinic.e,sizeof(ans));
}
else L=Mid+1;
}
if (flag) printf("%d\n",R+1);
else return !printf("NIE");
tot=0;
for (int i=1,j=0;i<=m;i++)
if (e[i].a<=R+1 && e[i].b<=R+1)
{
j++;
if (!ans[j*2].flow)
add2(e[i].to,e[i].from);
else
add2(e[i].from,e[i].to);
}
else if (e[i].a<=R+1)
add2(e[i].from,e[i].to);
else
add2(e[i].to,e[i].from);
print(1);
for (int i=m;i>=1;i--)
printf("%d ",st[i]);
return 0;
}