DarkBZOJ传送门
解析:
这绝对是我做过的最扯的欧拉回路的题了,明明标签是欧拉回路,可是怎么是一道网络流加二分答案啊?
思路:
首先,看到要求最大值最小,多半是个二分,然而。。。
二分之后验证什么?
好吧,看题目应该知道是要求欧拉回路。
那直接爆搜求欧拉回路?
用 (和谐) 想都知道是不可能的,怎么都能卡掉。
那怎么验证?
先想一想如何验证一般图是否存在欧拉回路:
1.无向图:一笔画问题相信大家都有所了解,只要不存在度数为奇数的节点就一定存在欧拉回路;
1.有向图:这个稍微有一点麻烦,但是结论说出来十分显然。一个有向图存在欧拉回路,当且仅当该图联通且所有节点入度等于出度。
读过刘汝佳……小蓝还是小紫来着,算了不重要……的读者可能见过一道题,直接求混合图的欧拉回路。
其实解法就是网络流。
我们只需要规定一些无向边的方向,使得这张图满足有向图存在欧拉回路的条件就行了。
至于怎么规定,总之要先将每一条边都有一个方向能够加入这张图才行。
我们先将正向反向都能够走的边加入原图,反正都是无向图,我们直接假设走的是风力小的方向,反正之后有办法处理。
首先这时候每个点的总度数必须是偶数,一旦是奇数肯定无解,然后我们将它需要将入边变向还是出边变向,分别向源汇连边,容量为出入度只差的绝对值的一半,表示我们需要将这条边的出入度调整多少才能相等。
这时候有向边就不用加进流网络的图里面了。
因为我们这时候是在调整无向边的方向,通过能否调整出合法情况来判断是否有解。合法的情况就是源汇都流满,说明差的流量能够被补上。
代码:
#include
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline ll getint(){
re ll num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=1003;
cs int M=4003;
cs int INF=0x3f3f3f3f;
int n,m;
int last[N],nxt[1000005],to[1000005],ecnt;
int cap[1000005];
inline void addedge(int u,int v,int val){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,cap[ecnt]=val;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,cap[ecnt]=0;
}
int u[M>>1],v[M>>1],c[M>>1],d[M>>1];
int S=0,T=1001;
int minn=INF,maxn=0;
int du[N],tot;
int lev[N],cur[N];
inline bool BFS(){
memset(lev,-1,sizeof lev);
queue<int> q;
q.push(S);lev[S]=0;cur[S]=last[S];
while(!q.empty()){
int u=q.front();q.pop();
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]==-1){
lev[v]=lev[u]+1;
if(v==T)return true;
cur[v]=last[v];
q.push(v);
}
}
}return false;
}
inline int Dinic(cs int &u,cs int &flow){
if(u==T)return flow;
int ans=0;
for(int &e=cur[u],v=to[e];e;v=to[e=nxt[e]]){
if(cap[e]&&lev[v]>lev[u]){
int delta=Dinic(v,min(flow-ans,cap[e]));
if(delta){
ans+=delta;
cap[e]-=delta;
cap[e^1]+=delta;
if(ans==flow)return ans;
}
}
}
return ans;
}
inline int maxflow(){
int ans=0;
while(BFS())ans+=Dinic(S,INF);
return ans;
}
inline void build(int mid){
memset(last,0,sizeof last);
memset(du,0,sizeof du);
ecnt=1;
tot=0;
for(int re i=1;i<=m;++i){
if(c[i]<=mid)--du[u[i]],++du[v[i]];
if(d[i]<=mid)addedge(v[i],u[i],1);
}
for(int re i=1;i<=n;++i)
if(du[i]>0)tot+=du[i]>>1,addedge(S,i,du[i]>>1);
else if(du[i]<0)addedge(i,T,(-du[i])>>1);
}
inline bool check(int mid){
build(mid);
for(int re i=1;i<=n;++i)if(du[i]&1)return false;
return maxflow()==tot;
}
signed main(){
n=getint();
m=getint();
for(int re i=1;i<=m;++i){
u[i]=getint(),v[i]=getint(),c[i]=getint(),d[i]=getint();
if(c[i]>d[i])swap(c[i],d[i]),swap(u[i],v[i]);
minn=min(c[i],minn);
maxn=max(d[i],maxn);
}
int l=minn,r=maxn;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid))r=mid-1;
else l=mid+1;
}
if(l==maxn+1)puts("NIE");
else cout<<l;
return 0;
}