CF527E Data Center Drama 题解

题目

CF527E Data Center Drama · 戳这里

题意

  • 给定一张 n n n 个点 m m m 条边的连通无向图。
  • 你需要加尽可能少的边,然后给所有边定向,使得每一个点的出入度都是偶数。
  • 边可以是自环,也可以有重边。
  • n ≤ 1 0 5 n \le 10^5 n105 m ≤ 2 × 1 0 5 m \le 2 \times 10^5 m2×105

(本题是 SPJ,所以顺序不用管)

题解

思路

所有顶点度数都为偶数,且该图是连通图,是无向图存在欧拉回路的充要条件。

所以我们需要将所有顶点度数为奇数的点两两相连,但是并不是所有存在欧拉回路的图都满足条件,还需要满足边数为偶数。

所以如果最后边数是奇数,随便找个点连个自环即可(这里就把 1 号节点连一个自环了)。

这显然是最少的加边方案,最后跑一个欧拉回路出来,然后隔一条边换一个方向即可。

详解

首先,存图我们用链式前向星存,然后在记录每个点的入度。

这里我们第一条边从 2 2 2 开始记,因为这样我们按顺序记录正着的边和反着的边,反着的边的编号就等于正着的边的编号异或 1 1 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);
}

然后记录一下入度为奇数的点。

我们用一个 v e c t o r vector 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 m m 是奇数,就给 1 1 1 号节点加个自环。

if(m & 1) {
  add(1, 1);
  ++m;
}

先输出一个边数 m 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

  • 15
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
美赛e题通常会要求解决一个实际的工程或管理问题,涉及到建模、数据分析、优化等方面。解题思路通常分为以下几个步骤: 1. 问题理解:首先要仔细阅读题目,理解问题背景、要求和限制条件。明确问题的目标和约束条件是解题的第一步。 2. 建立数学模型:根据问题要求和数据,建立合适的数学模型来描述问题。可能涉及到微积分、概率统计、线性代数等数学知识。模型的建立需要考虑到实际情况和假设条件,尽量简化和抽象问题方便求解。 3. 数据分析:根据所给数据或者自行获取数据,进行数据的处理和分析。可能需要进行数据清洗、统计分析、可视化等工作,以便更好地理解问题和验证模型。 4. 求解优化问题:根据建立的数学模型,可以采用各种优化方法来求解问题。常用的方法包括线性规划、整数规划、动态规划、遗传算法等。在求解的过程中需要注意约束条件的处理和结果的解释。 5. 结果分析和验证:得到结果后,需要进行结果的分析和验证,看是否符合实际情况和问题要求。如果有必要,可以进行灵敏度分析和稳定性分析,探讨模型的鲁棒性和可靠性。 6. 方案优化和改进:根据结果分析的情况,可以对建模和求解过程进行改进和优化,以提高解题的精度和效率。 以上是美赛e题解题思路的一般流程,具体问题具体分析,需要根据具体问题的特点来灵活应对。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值