【网络流】bzoj2095混合图欧拉回路

考虑到满足欧拉回路的条件是每个点的入度和出度相等。

答案可以二分,对于每个mid建图。

我们考虑如下的建图方式,先把所有的无向边随意定向,计算点的度数。如果此时有奇数度数的点,那么就肯定不存在欧拉回路。

S向入度<出度的点连容量为出度-入度的边,入度>出度的点向T连入度-出度的边,之前的随意定向的无向边提供了调整的机会,假设初始定向u->v,那么连v->u流量为1的边。

在最后我们需要找到欧拉路径即可

【代码】

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e4+5;
int n,m,u[maxn],v[maxn],val1[maxn],val2[maxn];
int fa[maxn],vis[maxn],block;
const int inf=0x3f3f3f3f;
int find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
int head[2005],tot;
struct edge
{
    int to,nxt,v;
}e[maxn<<2],E[maxn<<1];
int S,T,du[maxn];
void add(int x,int y,int val)
{
    e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=val; head[x]=tot;
}
int id[maxn<<1],dis[2005];
bool bfs()
{
    memset(dis,-1,sizeof(dis));
    queue <int> q;
    q.push(S); dis[S]=0;
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        {
            int to=e[i].to;
            if(dis[to]==-1 && e[i].v>0)
            {
                dis[to]=dis[u]+1;
                q.push(to);
            }
        }
    }
    return (dis[T]!=-1);
}
int dfs(int x,int flow)
{
    if(x==T) return flow;
    int res=0;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(e[i].v>0 && dis[to]==dis[x]+1)
        {
            int tmp=dfs(to,min(flow,e[i].v));
            flow-=tmp; e[i].v-=tmp; e[i^1].v+=tmp;
            res+=tmp;
        }
    }
    if(!res) dis[x]=-1;
    return res;
}
int dinic()
{
    int res=0;
    while(bfs())
    {
        res+=dfs(S,inf);
    }
    return res;
}
bool check(int mid)
{
    tot=1; memset(head,0,sizeof(head));
    memset(du,0,sizeof(du));
    for(int i=1;i<=n;i++) fa[i]=i;
    S=n+1; T=n+2; block=n;
    for(int i=1;i<=m;i++)
    {
        if(val1[i]<=mid) du[u[i]]--,du[v[i]]++;
        if(val2[i]<=mid)
        {
            id[i]=tot+1;
            add(v[i],u[i],1);
            add(u[i],v[i],0);
        }
        if(val1[i]<=mid || val2[i]<=mid)
        {
            int fu=find(u[i]),fv=find(v[i]);
            if(fu!=fv)
            {
                block--;
                fa[fu]=fv;
            }
        }
    }
    if(block>1)
        return false;
    for(int i=1;i<=n;i++) if(du[i]&1) return false;
    int maxflow=0;
    for(int i=1;i<=n;i++)
    {
        if(du[i]>0)
        {
            maxflow+=du[i]/2;
            add(S,i,du[i]/2); add(i,S,0);
        }
        else add(i,T,-du[i]/2),add(T,i,0);
    }
    if(dinic()==maxflow) return true;
    return false;
}
int h[2005],cnt;
void addedge(int x,int y,int z)
{
    E[++cnt].to=y; E[cnt].nxt=h[x]; E[cnt].v=z; h[x]=cnt;
}
int visit[2005],p[maxn],gs;
void Dfs(int u)
{
    for(int i=h[u];i;i=E[i].nxt)
    {
        // h[u]=i;
        if(visit[E[i].v]) continue;
        visit[E[i].v]=1;
        Dfs(E[i].to);
        p[++gs]=E[i].v;
    }
}
void print(int ans)
{
    check(ans);
    for(int i=1;i<=m;i++)
    {
        if(val1[i]<=ans && val2[i]<=ans)
        {
            if(e[id[i]].v==0) addedge(v[i],u[i],i);
            else addedge(u[i],v[i],i);
            continue;
        }
        else if(val1[i]<=ans) addedge(u[i],v[i],i);
        else if(val2[i]<=ans) addedge(v[i],u[i],i);
    }
    Dfs(1);
    for(int i=gs;i>=1;i--) printf("%d ",p[i]);
    printf("\n");
}
int main()
{
    // freopen("euler.in","r",stdin);
    // freopen("euler.out","w",stdout);
    scanf("%d%d",&n,&m);
    int l=inf,r=0,ans=-1;
    block=n;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&u[i],&v[i],&val1[i],&val2[i]);
        int fu=find(u[i]),fv=find(v[i]);
        if(fu!=fv)
        {
            block--;
            fa[fu]=fv;
        }
        vis[u[i]]=vis[v[i]]=1;
        if(val1[i]>val2[i])
        {
            swap(val1[i],val2[i]);
            swap(u[i],v[i]);
        }
        l=min(l,val1[i]); r=max(r,val2[i]);
    }
    int single=0;
    for(int i=1;i<=n;i++) if(!vis[i]) single++;
    if(block>1 && block-single!=1)
    {
        printf("NIE");
        return 0;
    }
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid))
        {
            ans=mid;
            r=mid-1;
        }
        else l=mid+1;
    }
    if(ans==-1)
    {
        printf("NIE");
        return 0;
    }
    printf("%d\n",ans);
    print(ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值