CodeForces 1610F Mashtali: a Space Oddysey

洛谷传送门

CF 传送门

比较有启发性的题。

首先,设 \(a_u\) 为与点 \(u\) 相连的边权和,答案的上界显然是 \(\sum\limits_{i = 1}^n [a_u \bmod 2 = 1]\)

之后我们把 P7816 「Stoi2029」以父之名第一篇题解的做法搬过来,也就是:

  1. 建一个虚点向原图度数为奇数的点连边权为 \(1\) 的边,以保证新图中每个点的度数都为偶数,
  2. 在新图上直接跑欧拉回路,顺便根据访问的方向给每条边定向。注意进入一个点时优先选择与入边边权相同的边出去,再选择不同的。

可以证明这样的构造方法能达到答案上界。对于 \(a_u \bmod 2 = 1\) 的点:

  • \(deg_u \bmod 2 = 1\),也就是它向虚点连边,那么连完边后与 \(u\) 相连的边权为 \(1\)\(2\) 的边的数量都为偶数,也就是我们总是能选择一条与入边边权相同的出边。这样减去向虚点连的边,符合条件。
  • \(deg_u \bmod 2 = 0\),也就是它不向虚点连边,那么在原图中与 \(u\) 相连的边权为 \(1\)\(2\) 的边的数量都为奇数。这样我们会恰好有一次入边边权与出边边权不等的情形,也满足条件。

对于 \(a_u \bmod 2 = 0\) 的点,因为它本来就不可能满足条件,所以不用管它。

于是我们就证明了这个构造方法是正确的。

注意图可能不连通。

code
// Problem: F. Mashtali: a Space Oddysey
// Contest: Codeforces - Codeforces Global Round 17
// URL: https://codeforces.com/problemset/problem/1610/F
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 1000100;

int n, m, deg[maxn], a[maxn], ans[maxn], b[maxn];
bool vis[maxn];
vector<pii> G[maxn][3];

void dfs(int u, int k) {
	vis[u] = 1;
	for (int t = k, o = 0; o < 2; ++o, t ^= 3) {
		while (G[u][t].size()) {
			pii p = G[u][t].back();
			G[u][t].pop_back();
			int v = p.fst, d = p.scd;
			if (ans[d]) {
				continue;
			}
			ans[d] = (u == b[d] ? 1 : 2);
			dfs(v, t);
		}
	}
}

void solve() {
	scanf("%d%d", &n, &m);
	for (int i = 1, u, v, d; i <= m; ++i) {
		scanf("%d%d%d", &u, &v, &d);
		++deg[u];
		++deg[v];
		a[u] += d;
		a[v] += d;
		G[u][d].pb(v, i);
		G[v][d].pb(u, i);
		b[i] = u;
	}
	for (int i = 1; i <= n; ++i) {
		if (deg[i] & 1) {
			G[i][1].pb(n + 1, m + i);
			G[n + 1][1].pb(i, m + i);
		}
	}
	for (int i = 1; i <= n; ++i) {
		if (!vis[i]) {
			dfs(i, 1);
		}
	}
	int cnt = 0;
	for (int i = 1; i <= n; ++i) {
		cnt += (a[i] & 1);
	}
	printf("%d\n", cnt);
	for (int i = 1; i <= m; ++i) {
		printf("%d", ans[i]);
	}
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2023-08-11 22:26  zltzlt  阅读(11)  评论(0编辑  收藏  举报