@
目录题目
CF527E Data Center Drama · 戳这里
题意
- 给定一张 $n$ 个点 $m$ 条边的连通无向图。
- 你需要加尽可能少的边,然后给所有边定向,使得每一个点的出入度都是偶数。
- 边可以是自环,也可以有重边。
- $n \le 10^5$,$m \le 2 \times 10^5$。
(本题是 SPJ,所以顺序不用管)
题解
思路
所有顶点度数都为偶数,且该图是连通图,是无向图存在欧拉回路的充要条件。
所以我们需要将所有顶点度数为奇数的点两两相连,但是并不是所有存在欧拉回路的图都满足条件,还需要满足边数为偶数。
所以如果最后边数是奇数,随便找个点连个自环即可(这里就把 1 号节点连一个自环了)。
这显然是最少的加边方案,最后跑一个欧拉回路出来,然后隔一条边换一个方向即可。
详解
首先,存图我们用链式前向星存,然后在记录每个点的入度。
这里我们第一条边从 $2$ 开始记,因为这样我们按顺序记录正着的边和反着的边,反着的边的编号就等于正着的边的编号异或 $1$。
int edge_tot = 1;
int in_cnt[N];
int head[N];
struct Edge {
int to;
int nxt;
};
Edge edge[N];
void add(int u, int v) {
++edge_tot;
edge[edge_tot].to = v;
edge[edge_tot].nxt = head[u];
head[u] = edge_tot;
++in_cnt[v];
}
接下来是主函数的输入部分。
int n, m;
int u, v;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
head[i] = -1;
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
然后记录一下入度为奇数的点。
我们用一个 $vector$ 来存。
vector <int> ill;
然后将所有入度为奇数的点都压进去。
for(int i = 1; i <= n; ++i)
if(in_cnt[i] & 1)
ill.push_back(i);
再把它们两两相连。
for(int i = 0; i < ill.size(); i += 2) {
add(ill[i], ill[i + 1]);
add(ill[i + 1], ill[i]);
++m;
}
然后判断如果这时候边数 $m$ 是奇数,就给 $1$ 号节点加个自环。
if(m & 1) {
add(1, 1);
++m;
}
先输出一个边数 $m$。
printf("%d\n", m);
然后就是输出边了,这里跑个欧拉回路就行了。
bool vis[N];
int print_tot;
void dfs(int u) {
for(int &i = head[u]; i != -1; ) {
int v = edge[i].to;
if(vis[i]) {
i = edge[i].nxt;
continue;
}
vis[i] = vis[i ^ 1] = true;
i = edge[i].nxt;
dfs(v);
++print_tot;
if(print_tot & 1)
printf("%d %d\n", u, v);
else
printf("%d %d\n", v, u);
}
}
注意事项
-
for(int &i = head[u]; i != -1; )
里的&i
。 -
i = edge[i].nxt;
要写两遍,不能提到前面,否则后面的i
就都变了。
代码
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
int n, m;
int u, v;
int edge_tot = 1;
int in_cnt[N];
vector <int> ill;
bool vis[N];
int print_tot;
int head[N];
struct Edge {
int to;
int nxt;
};
Edge edge[N];
void add(int u, int v) {
++edge_tot;
edge[edge_tot].to = v;
edge[edge_tot].nxt = head[u];
head[u] = edge_tot;
++in_cnt[v];
}
void dfs(int u) {
for(int &i = head[u]; i != -1; ) {
int v = edge[i].to;
if(vis[i]) {
i = edge[i].nxt;
continue;
}
vis[i] = vis[i ^ 1] = true;
i = edge[i].nxt;
dfs(v);
++print_tot;
if(print_tot & 1)
printf("%d %d\n", u, v);
else
printf("%d %d\n", v, u);
}
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i)
head[i] = -1;
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
}
for(int i = 1; i <= n; ++i)
if(in_cnt[i] & 1)
ill.push_back(i);
for(int i = 0; i < ill.size(); i += 2) {
add(ill[i], ill[i + 1]);
add(ill[i + 1], ill[i]);
++m;
}
if(m & 1) {
add(1, 1);
++m;
}
printf("%d\n", m);
dfs(1);
return 0;
}
AC 记录
尾声
如果这篇博客对您(您的团队)有帮助的话,就帮忙点个赞,加个关注!
最后,祝您(您的团队)在 OI 的路上一路顺风!!!
┬┴┬┴┤・ω・)ノ ByeBye
标签:nxt,head,CF527E,题解,++,tot,Drama,int,edge From: https://www.cnblogs.com/pangyuchen75-blog/p/18199353