【YBT2022寒假Day3 B】【LOJ 2460】欧拉回路 / 桥(二分)(欧拉回路)(网络流)

欧拉回路 / 桥

题目链接:YBT2022寒假Day3 B / LOJ 2460

题目大意

给你一个图,边是双向且两边走的费用不同。
要你选一些边来走,然后使得形成一条欧拉回路,且所选边的最大费用最小。
要输出这个费用和选的边。

思路

首先这个最大费用最小直接用二分。

那么接着就是判断了,我们可以把边分成两类:
只能有一个方向的和两个方向都可以的。(如果有两边的都走不了肯定不行,因为你欧拉回路要经过所有边)
那只有一个方向的就直接确定了,两个方向的就不确定。

先看如果判断是否形成欧拉回路,即要每个点的入度和出度相同。
如果你考虑用网络流来选择两个方向的,你会发现它会出现流了一半的问题。

所以就有一个小方法:你先随便选一个方向,然后如果选回去那就是一个点入度减 2,出度加 2,另一个点反过来。
那这样每次要么 0 要么 2,我们可以集体把流量都 /2 变成 0,1,这样就不会出现流一半了。
具体来讲,你选边先,然后每个点有一个 Δ 就是入度和出度的差,如果 Δ>0 就从源点向它连流量为 Δ2,如果 <0 就连到汇点流量为 Δ2
(如果 Δ 是奇数就肯定不行但是不难看出肯定是偶数)
然后至于双向都可以走的边 (x,y),一开始用的是 xy,那就从 yx 连一条边流量为 1

然后判断既是看这些从源点汇点连的边是否都流满了。

然后注意判 NIE,就是如果不连通是 NIE,原本的无向图都没有欧拉回路是 NIE。

代码

#include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #define INF 0x3f3f3f3f3f3f3f3f using namespace std; struct node { int x, y, v1, v2; }a[2001]; int n, m, L, R, ans; int ru[2001], chu[2001]; int fa[2001], S, T, tot; int dy[2001]; vector <int> b[2001], goo[2001], pl[2001], answ; struct water { int x, to, nxt, op, fr; }e[2000001]; int le[2001], KK, lee[2001]; int find(int now) { if (fa[now] == now) return now; return fa[now] = find(fa[now]); } void merge(int x, int y) { int X = find(x), Y = find(y); if (X == Y) return ; fa[X] = Y; } void Add(int x, int y, int z, int k) { e[++KK] = (water){z, y, le[x], KK + 1, k}; le[x] = KK; e[++KK] = (water){0, x, le[y], KK - 1, k}; le[y] = KK; } queue <int> q; int dis[2001]; bool bfs() { while (!q.empty()) q.pop(); memset(dis, 0, sizeof(dis)); for (int i = 1; i <= tot; i++) lee[i] = le[i]; dis[S] = 1; q.push(S); while (!q.empty()) { int now = q.front(); q.pop(); for (int i = le[now]; i; i = e[i].nxt) if (e[i].x && !dis[e[i].to]) { dis[e[i].to] = dis[now] + 1; if (e[i].to == T) return 1; q.push(e[i].to); } } return 0; } int dfs(int now, int sum) { if (now == T) return sum; int go = 0; for (int &i = lee[now]; i; i = e[i].nxt) if (e[i].x && dis[e[i].to] == dis[now] + 1) { int this_go = dfs(e[i].to, min(e[i].x, sum - go)); if (this_go) { e[i].x -= this_go; e[e[i].op].x += this_go; go += this_go; if (go == sum) return go; } } if (go != sum) dis[now] = -1; return go; } int Dinic() { int re = 0; while (bfs()) re += dfs(S, INF); return re; } bool check(int k) { KK = 0; memset(le, 0, sizeof(le)); tot = n; S = ++tot; T = ++tot; memset(ru, 0, sizeof(ru)); memset(chu, 0, sizeof(chu)); for (int i = 1; i <= m; i++) { if (a[i].v1 <= k && a[i].v2 <= k) { chu[a[i].x]++; ru[a[i].y]++; Add(a[i].y, a[i].x, 1, i); } else if (a[i].v1 <= k) { chu[a[i].x]++; ru[a[i].y]++; } else { chu[a[i].y]++; ru[a[i].x]++; } } int sum = 0; for (int i = 1; i <= n; i++) { int derta = ru[i] - chu[i]; if (derta > 0) Add(S, i, derta / 2, 0), sum += derta / 2; else if (derta < 0) Add(i, T, -derta / 2, 0); } int nowsum = Dinic(); return nowsum == sum; } int num; bool in[2001]; void Runway(int now) { for (int i = 0; i < b[now].size(); i++) { if (goo[now][i]) continue; else { goo[now][i] = 1; Runway(b[now][i]); answ.push_back(pl[now][i]); } } } int main() { // freopen("euler.in", "r", stdin); // freopen("euler.out", "w", stdout); scanf("%d %d", &n, &m); for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 1; i <= m; i++) { scanf("%d %d %d %d", &a[i].x, &a[i].y, &a[i].v1, &a[i].v2); L = max(L, min(a[i].v1, a[i].v2)); R = max(R, max(a[i].v1, a[i].v2)); ru[a[i].x]++; ru[a[i].y]++; merge(a[i].x, a[i].y); } for (int i = 1; i <= n; i++) if (find(i) != find(1)) { printf("NIE"); return 0; } for (int i = 1; i <= n; i++) if (ru[i] & 1) { printf("NIE"); return 0; } else ru[i] = 0; while (L <= R) { int mid = (L + R) >> 1; if (check(mid)) ans = mid, R = mid - 1; else L = mid + 1; } printf("%d\n", ans); check(ans); for (int i = 1; i <= m; i++) { if (a[i].v1 <= ans && a[i].v2 <= ans) { } else if (a[i].v1 <= ans) { b[a[i].x].push_back(a[i].y); goo[a[i].x].push_back(0); pl[a[i].x].push_back(i); } else { b[a[i].y].push_back(a[i].x); goo[a[i].y].push_back(0); pl[a[i].y].push_back(i); } } for (int x = 1; x <= n; x++) { for (int i = le[x]; i; i = e[i].nxt) if (e[i].fr && !e[i].x) { int y = e[i].to; b[x].push_back(y); goo[x].push_back(0); pl[x].push_back(e[i].fr); } } Runway(1); for (int i = answ.size() - 1; i >= 0; i--) printf("%d ", answ[i]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT2022Day3_B.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-02-08 GDKOI2021 爆炸记
点击右上角即可分享
微信分享提示