考试题(欧拉回路)

悠木皆守学会了一种古老的文字。
该文字共有M 种不同的字符1; 2; : : : ; M。一个简单词由两个字符组
成(这两个字符可以相同)。一个复杂词由若干个简单词组合而成;具体地,
对于一个词(可以是简单词或复杂词)a1a2 · · · al 和另一个简单词b1b2,如果
al = b1,那么它们可以组合成一个复杂词a1a2 · · · alb2(因为al = b1 所以b1
被去掉了)。例如,如果用英文字母来表示字符,ab 和bc 可以组合成abc。
现在,悠木皆守有N 个简单词,第i 个词是uivi,他想用每个词恰好一次
(顺序任意),将它们组合起来形成一个复杂词(注意,如果输入的简单词有相
同的,那么也要每个用一次)。
另外再输入一个参数T。若T = 1,则一个简单词的两个字符可以交换顺
序,即uivi 可以当做viui 来使用(但只能使用这两个中的一个);如果T = 2,
则一个简单词的两个字符不能交换顺序,只能作为uivi 使用。

 

 

这道题首先数据范围1e5 的 2e5,如果n ^ 2的建图肯定会T,那么可以考虑把每个单词看作边,把数字看作点,这样就变成了求一个路径能够恰好覆盖所有边一次的,那么就变成的欧拉路。

呃呃呃考虑如何快速的求欧拉回路,这里有一篇题解讲的非常好https://www.cnblogs.com/ivorysi/p/5745005.html

这里有个套圈法,每次先走到一个不能再走的废物节点,然后把边/点压进一个栈来存储,这样一直找,最后倒序把栈中元素输出就是答案了

判断不合法:

对于无向图,首先图要联通,然后要判断奇点,若数量 > 2 或  =1,那么不合法

要不然就有两个奇点或没有,有两个的时候一个作为出发点,一个作为终点

没有的时候还是欧拉回路,随便找一个点作为起点即可

对于有向图,图还是要联通,但是判断图联通不能随便选出发点了,要在找到合法出发点再进行dfs记录数量,像无向图一样使用套圈法记录回路即可

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn = 8e5 + 5;

int T, n, m;

int read() {
	int rt = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		rt = (rt << 1) + (rt << 3) + ch - '0';
		ch = getchar();
	}
	return rt * f;
}

int cnt, nxt[maxn], tot = 1, tov[maxn], head[maxn], id[maxn];

int dgr[maxn], tmp[maxn], stk[maxn], s, top = 0, in[maxn], out[maxn];

bool vis[maxn], ar[maxn];

struct Node {
	int u, v;
}a[maxn];

void add(int u, int v, int w) {
	tot++;
	tov[tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
	id[tot] = w; 
}

void dfs(int u) {
	vis[u] = 1;
	cnt++;
	for (int i = head[u]; i; i = nxt[i]) {
		int v = tov[i];
		if (vis[v]) continue;
		dfs(v);
	}
}

void dfs1(int u, int e) {
	for (int i = head[u]; i; i = nxt[i]) {
		int v = tov[i];
		if (vis[i]) continue;
		vis[i] = vis[i ^ 1] = 1;
		dfs1(v, i);
	}
	stk[++top] = id[e];
} 

void dfs2(int u, int e) {
	if (!ar[u]) ar[u] = 1, cnt++;
	for (int i = head[u]; i; i = nxt[i]) {
		int v = tov[i];
		if (vis[i]) continue;
		vis[i] = 1;
		dfs2(v, i);
	}
	stk[++top] = id[e];
} 

int main() {
	freopen("merge.in","r",stdin);
	freopen("merge.out","w",stdout);
	T = read(), m = read(), n = read();
	if (T == 1) {
		int cnt1 = 0;
		for (int i = 1; i <= n; i++) {
			a[i].u = read(), a[i].v = read();
			if (!vis[a[i].u]) vis[a[i].u] = 1, cnt1++;
			if (!vis[a[i].v]) vis[a[i].v] = 1, cnt1++;
			add(a[i].u, a[i].v, i);
			add(a[i].v, a[i].u, -i);
			dgr[a[i].u]++;
			dgr[a[i].v]++;
		}
		for (int i = 1; i <= tot; i++) vis[i] = 0;
		cnt = 0;
		dfs(a[1].u);
		if (cnt != cnt1) {
			puts("NO");
			return 0;
		}
		for (int i = 1; i <= m; i++) vis[i] = 0;
		int num = 0;
		for (int i = 1; i <= n; i++) {
			if ((dgr[a[i].u] & 1) && !vis[a[i].u]) tmp[++num] = a[i].u, vis[a[i].u] = 1;
			if ((dgr[a[i].v] & 1) && !vis[a[i].v]) tmp[++num] = a[i].v, vis[a[i].v] = 1;
		}
		if (num == 0) {
			s = a[1].u;
		} else if (num == 2) {
			s = tmp[1];
		} else {
			puts("NO");
			return 0;
		}
		for (int i = 1; i <= tot; i++) vis[i] = 0;
		dfs1(s, 0);
		puts("YES");
		for (int i = top - 1; i >= 1; i--) {
			printf("%d", stk[i]);
			if (i != 1) printf(" ");
		}
	} else {
		int cnt1 = 0;
		for (int i = 1; i <= n; i++) {
			a[i].u = read(), a[i].v = read();
			if (!vis[a[i].u]) vis[a[i].u] = 1, cnt1++;
			if (!vis[a[i].v]) vis[a[i].v] = 1, cnt1++;
			add(a[i].u, a[i].v, i);
			in[a[i].v]++, out[a[i].u]++;
		}
		cnt = 0;
		int num = 0;
		bool flag = 0;
		for (int i = 1; i <= m; i++) vis[i] = 0;
		for (int i = 1; i <= n; i++) {
			if (in[a[i].u] != out[a[i].u]) {
				if (in[a[i].u] != out[a[i].u] + 1 && in[a[i].u] != out[a[i].u] - 1) flag = 1;
				if (!vis[a[i].u])
				tmp[++num] = a[i].u, vis[a[i].u] = 1;
			}
			if (in[a[i].v] != out[a[i].v]) {
				if (in[a[i].v] != out[a[i].v] + 1 && in[a[i].v] != out[a[i].v] - 1) flag = 1;
				if (!vis[a[i].v])
				tmp[++num] = a[i].v, vis[a[i].v] = 1;
			}
		}
		for (int i = 1; i <= tot; i++) vis[i] = 0;
		if (flag) {
			puts("NO");
			return 0;
		}
		if (num == 0) {
			s = a[1].u;
		} else if (num == 2) {
			if (in[tmp[1]] == out[tmp[1]] - 1) s = tmp[1];
			if (in[tmp[2]] == out[tmp[2]] - 1) s = tmp[2];
			if (in[tmp[1]] == out[tmp[1]] - 1 && in[tmp[2]] == out[tmp[2]] - 1) flag = 1; 
			if (in[tmp[1]] == out[tmp[1]] + 1 && in[tmp[2]] == out[tmp[2]] + 1) flag = 1;
		} else {
			puts("NO");
			return 0;
		}
		if (flag) {
			puts("NO");
			return 0;
		}
		dfs2(s, 0);
		if (cnt != cnt1) {
			puts("NO");
			return 0;
		}
		puts("YES");
		for (int i = top - 1; i >= 1; i--) {
			printf("%d", stk[i]);
			if (i != 1) printf(" ");
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值