• 【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;
    }
    
  • 相关阅读:
    沈阳集训day2
    ac自动机
    2018沈阳集训day1
    洛谷P1875 佳佳的魔法药水
    洛谷P1941 飞扬的小鸟
    Noip2016day2
    1123: [POI2008]BLO
    1718: [Usaco2006 Jan] Redundant Paths 分离的路径
    P3119 [USACO15JAN]草鉴定Grass Cownoisseur
    [LeetCode] Clone Graph
  • 原文地址:https://www.cnblogs.com/stoorz/p/12758701.html
Copyright © 2020-2023  润新知