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

欧拉回路 / 桥

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

题目大意

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

思路

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

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

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

所以就有一个小方法:你先随便选一个方向,然后如果选回去那就是一个点入度减 \(2\),出度加 \(2\),另一个点反过来。
那这样每次要么 \(0\) 要么 \(2\),我们可以集体把流量都 \(/2\) 变成 \(0,1\),这样就不会出现流一半了。
具体来讲,你选边先,然后每个点有一个 \(\Delta\) 就是入度和出度的差,如果 \(\Delta >0\) 就从源点向它连流量为 \(\dfrac{\Delta}{2}\),如果 \(<0\) 就连到汇点流量为 \(\dfrac{-\Delta}{2}\)
(如果 \(\Delta\) 是奇数就肯定不行但是不难看出肯定是偶数)
然后至于双向都可以走的边 \((x,y)\),一开始用的是 \(x\rightarrow y\),那就从 \(y\)\(x\) 连一条边流量为 \(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;
}
posted @ 2022-02-08 10:35  あおいSakura  阅读(38)  评论(0编辑  收藏  举报