Atcoder Grand Contest 025 E - Walking on a Tree(欧拉回路)
作者:互联网
打个表发现答案等于每条边被覆盖的次数与 \(2\) 取 min 之和,考虑如何构造这个上界。
首先考虑树是以 \(1\) 为中心的菊花图,且任意 \(A_i,B_i\ne 1\) 的做法:我们考虑在 \(A_i,B_i\) 之间连边。那么发现限制等价于,对每条边定向使得每个度 \(\ge 2\) 的点至少有一个出边和一个出边,这是经典欧拉回路问题,只需要让每个点入度和出度差 \(\le 1\) 即可满足条件,直接建虚点连向所有奇度点,然后对每个连通块做欧拉回路即可。
实际上这个做法对于每个点 \(x\) 恰有偶数个 \(i\) 满足 \(A_i=x\) 或 \(B_i=x\) 的情况也是成立的。因为这意味着我们在 \(A_i,B_i\) 之间连边后,连出来的图中每个连通块都是欧拉图。那么我们考察每条“存在至少一个路径 \(A_i\to B_i\) 经过”的边 \((u,v)\),我们假设断开这条边后树形成的两个点集为 \(S,T\),那么欧拉回路的一个性质是对于一组满足 \(S\cap T=\varnothing,S\cup T=V\) 的 \(S,T\),\(S\to T\) 的边数与 \(T\to S\) 的边数相同,这容易通过欧拉回路的性质说明,这也就意味着至少存在一条路径满足其经过 \(u,v\) 时是 \(u\to v\),也至少存在一条 \(v\to u\) 的路径。也就自然构造到了上界。
考虑如果出现奇度点怎么办,我们考虑从上到下 DFS,对于一个点 \(x\) 的某个儿子 \(y\),如果 \(y\) 目前度数是奇数,那么加入路径 \((x,y)\),容易证明这样构造的正确性。
时间复杂度 \(n\log n\)。
#include <bits/stdc++.h>
// #include <ext/pb_ds/assoc_container.hpp>
// #include <ext/pb_ds/hash_policy.hpp>
// #include <ext/pb_ds/priority_queue.hpp>
using namespace std;
// using namespace __gnu_pbds;
#define fi first
#define se second
#define fill0(a) memset(a, 0, sizeof(a))
#define fill1(a) memset(a, -1, sizeof(a))
#define fillbig(a) memset(a, 63, sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
#define mt make_tuple
#ifdef LOCAL
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define eprintf(...) 1064
#endif
template<typename T1, typename T2> void chkmin(T1 &x, T2 y) {if (x > y) x = y;}
template<typename T1, typename T2> void chkmax(T1 &x, T2 y) {if (x < y) x = y;}
typedef pair<int, int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long double ld;
#ifdef FASTIO
#define FILE_SIZE 1 << 23
char rbuf[FILE_SIZE], *p1 = rbuf, *p2 = rbuf, wbuf[FILE_SIZE], *p3 = wbuf;
#ifdef LOCAL
inline char getc() {return getchar();}
inline void putc(char c) {putchar(c);}
#else
inline char getc() {return p1 == p2 && (p2 = (p1 = rbuf) + fread(rbuf, 1, FILE_SIZE, stdin), p1 == p2) ? -1 : *p1++;}
inline void putc(char x) {*p3++ = x;}
#endif
template<typename T> void read(T &x) {
x = 0; char c = getc(); T neg = 0;
while (!isdigit(c)) neg |= (c == '-'), c = getc();
while (isdigit(c)) x = x * 10 + (c - '0'), c = getc();
if (neg) x = -x;
}
template<typename T> void recursive_print(T x) {
if (!x) return;
recursive_print(x / 10); putc(x % 10 ^ 48);
}
template<typename T> void print(T x) {
if (!x) putc('0'); if (x < 0) putc('-'), x = -x;
recursive_print(x);
}
template<typename T> void print(T x, char c) {print(x); putc(c);}
void readstr(char *s) {
char c = getc();
while (c <= 32 || c >= 127) c = getc();
while (c > 32 && c < 127) s[0] = c, s++, c = getc();
(*s) = 0;
}
void printstr(string s) {for (int i = 0; i < s.size(); i++) putc(s[i]);}
void printstr(char *s) {
int len = strlen(s);
for (int i = 0; i < len; i++) putc(s[i]);
}
void print_final() {fwrite(wbuf, 1, p3 - wbuf, stdout);}
#endif
const int MAXN = 1e5;
const int LOG_N = 18;
const int MAXE = 1e6;
int n, m, A[MAXN + 5], B[MAXN + 5]; vector<int> g[MAXN + 5];
int fa[MAXN + 5][LOG_N + 2], dep[MAXN + 5];
void dfs0(int x, int f) {
fa[x][0] = f;
for (int y : g[x]) {
if (y == f) continue; dep[y] = dep[x] + 1;
dfs0(y, x);
}
}
int getlca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = LOG_N; ~i; i--) if (dep[x] - (1 << i) >= dep[y]) x = fa[x][i];
if (x == y) return x;
for (int i = LOG_N; ~i; i--) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int mark[MAXN + 5], deg[MAXN + 5], hd[MAXN + 5], to[MAXE + 5], nxt[MAXE + 5], ec = 1;
void adde(int u, int v) {to[++ec] = v; nxt[ec] = hd[u]; hd[u] = ec;}
void dfs1(int x, int f) {
for (int y : g[x]) {
if (y == f) continue; dfs1(y, x); mark[x] += mark[y];
if (deg[y]) deg[x] ^= 1, deg[y] ^= 1, adde(x, y), adde(y, x);
}
}
int dir[MAXE + 5], vis[MAXN + 5];
void dfs2(int x) {
vis[x] = 1;
for (int &e = hd[x]; e; e = nxt[e]) if (dir[e >> 1] == -1)
dir[e >> 1] = e & 1, dfs2(to[e]);
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#else
freopen("path.in", "r", stdin);
freopen("path.out", "w", stdout);
#endif
scanf("%d%d", &n, &m);
for (int i = 1, u, v; i < n; i++) scanf("%d%d", &u, &v), g[u].pb(v), g[v].pb(u); dfs0(1, 0);
for (int i = 1; i <= LOG_N; i++) for (int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1];
for (int i = 1; i <= m; i++) {
scanf("%d%d", &A[i], &B[i]); int lc = getlca(A[i], B[i]);
mark[A[i]]++; mark[B[i]]++; mark[lc] -= 2; adde(A[i], B[i]); adde(B[i], A[i]);
deg[A[i]] ^= 1; deg[B[i]] ^= 1;
}
dfs1(1, 0); memset(dir, -1, sizeof(dir));
for (int i = 1; i <= n; i++) if (!vis[i]) dfs2(i);
int res = 0; for (int i = 1; i <= n; i++) res += min(mark[i], 2);
printf("%d\n", res);
for (int i = 1; i <= m; i++) {
if (dir[i]) printf("%d %d\n", A[i], B[i]);
else printf("%d %d\n", B[i], A[i]);
}
return 0;
}
标签:Atcoder,Walking,Contest,int,void,fa,MAXN,print,define 来源: https://www.cnblogs.com/ET2006/p/agc025E.html