20211114 地图 map
Description
给定一个 \(n\) 个点 \(m\) 条边的无向图,现在有两种颜色,对每条边定色,使得每个点连的两种颜色的边数量差小于等于 \(1\) 。
\(n\ , m\leq 2\cdot 10 ^6\)
Analysis
题意就是每条边要么给连向的两个点答案 \(+1\) ,要么同时 \(-1\) ,问能不能让每个点的答案的绝对值不超过 \(1\) 。
刚拿到这个题的时候第一感觉是 eugene ,但是那个是定向,连向的两个点一边 \(-1\) , \(+1\) ,这个是同时 \(+1\) 或 \(-1\) ,有点关系,做法也有点像,但那个是加强版,就不去管他。
这里有个比较显眼的一个 Sub ,注意到 Subtask 3 的图好像是个欧拉回路(连不连通问题不大),然后我们想到欧拉回路上的边如果是前面一条 \(-1\) ,后面一条 \(+1\) ,这样对于每个点,无论怎么样前后两条边的答案都会抵消。
然后发现一个那啥边数为奇数的欧拉回路无论怎么 \(+1\) 或 \(-1\) 都至少会有一个点前后两条边都是 \(+1\) 或 \(-1\) ,这种就肯定无解,判掉就行了。
Solution
前两个 Sub 和正解没啥关系,就不去管他了。
接着 Analysis 的想法,发现一般的图就是有一些度数为奇数的点,想想怎么去避免。
删边的话不太现实,不知道删那些,干脆加边,加边,加边!而且就算有重边的话,遍历的时候肯定会挨在一起,又因为我们是在路径上 \(01\) 染色,所以就算实际上得路径不合法,这重边的贡献也会相互抵消掉,本质上也不会影响最终欧拉回路的结果。
然后发现这样的话,因为是无向图,所以无论怎么样都会有偶数个度数为奇数的点,每个点只需要额外连出去一条边。加上跑完欧拉回路后每个点的答案都是 \(0\) ,所以把多加的边删掉之后答案也不会超过 \(1\) ,肯定是满足的。
但是 Analysis 里面也说了如果是边数为奇数的欧拉回路不能做到全部答案为 \(0\) 。虽然一般的情况我们会最终去掉一些边,可能能让这个答案合法,但是为了严谨性,我们不这样去做。
那怎么办呢?啊啊啊!怎么办呀!!
这每次加边的话只加一条,可以调整边数奇偶性,然后可以开一个虚点,每次连两条边出去,这样边数奇偶性就有了保障,就可以放心的跑欧拉回路了!
时间复杂度 \(O(n)\) 。
Code
/*
5 5
1 2
2 3
3 4
4 5
5 3
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e6 + 10;
int n, _m, m, du[N], fst[N], tot = 1, cur[N], ans[N << 1];
bool vis[N << 1], sig[N], ljp;
struct edge {
int nxt, fr, to;
} e[N << 1];
inline int read() {
char ch = getchar();
int s = 0, w = 1;
while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {s = (s << 3) + (s << 1) + ch - '0'; ch = getchar();}
return s * w;
}
inline void add(int u, int v) {
e[++tot] = (edge) {fst[u], u, v};
fst[u] = tot;
}
inline void dfs_ola(int u, int las) {
sig[u] = 1;
for (int i = cur[u]; i; i = cur[u]) {
cur[u] = e[i].nxt;
int v = e[i].to;
if (vis[i ^ 1] || vis[i]) continue;
vis[i] = 1;
dfs_ola(v, i);
}
ans[las >> 1] = ljp; ljp ^= 1;
}
int main() {
// freopen("map.in", "r", stdin);
// freopen("map.out", "w", stdout);
n = read(); _m = m = read();
for (int i = 1, u, v; i <= m; ++i) {
u = read(), v = read();
add(u, v); ++du[u];
add(v, u); ++du[v];
}
int now = 0;
for (int i = 1; i <= n; ++i) if (du[i] & 1) {
if (!now) now = i;
else {
if (m & 1) {
add(i, now); add(now, i);
++du[i]; ++du[now]; ++m; now = 0;
}
else {
add(now, n + 1); add(n + 1, now);
add(i, n + 1); add(n + 1, i);
++du[i]; ++du[now]; m += 2; now = 0;
}
}
}
if (m & 1) {
printf("-1\n");
return 0;
}
for (int i = 1; i <= n + 1; ++i) cur[i] = fst[i];
for (int i = 1; i <= n; ++i) {
if (!sig[i]) dfs_ola(i, 0);
}
for (int i = 1; i <= _m; ++i) {
printf("%d ", ans[i]);
}
return 0;
}