欧拉路径

\(\text{uva-10054}\)

给定 \(n\) 条边的图,判断该图是否是欧拉图,若是则输出欧拉回路;否则报告无解。

\(5 \le n \le 1000\)\(1 \le u_i, v_i \le 50\)


模板题,一种比较简单的求解方式,用邻接矩阵存图即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 1005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long T, n, v[MAXN][MAXN], d[MAXN], cnt;

void dfs(long long x) {
	for(int i = 1; i <= 50; i ++) if(v[x][i]) {
		v[x][i] --, v[i][x] --;
		dfs(i); cout << i << " " << x << "\n";
	}
	return;
}

int main() {
	T = read();
	while(T --) {
		n = read(); long long s = 0, fg = 1;
		for(int i = 1; i <= 50; i ++) for(int j = 1; j <= 50; j ++) v[i][j] = 0;
		for(int i = 1; i <= 50; i ++) d[i] = 0;
		for(int i = 1; i <= n; i ++) {
			long long x = read(), y = read();
			v[y][x] ++, v[x][y] ++;
			d[x] ++, d[y] ++, s = x;
		}
		for(int i = 1; i <= 50; i ++) if(d[i] % 2) fg = 0;
		cout << "Case #" << (++ cnt) << "\n";
		if(!fg) cout << "some beads may be lost\n";
		else dfs(s);
		cout << "\n";
	}
	return 0;
}

\(\text{luogu-6066}\)

给定 \(n\) 个节点 \(m\) 条边的无向图,输出一条路径使得每条边都恰好走两边。

\(1 \le m \le 5 \times 10^4\)\(2 \le n \le 10^4\)


由于恰好走两边,于是这个东西比欧拉回路的限制还宽泛,直接 \(\text{dfs}\) 即可。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAXN 10005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

vector<long long> v[MAXN];
long long n, m;

void dfs(long long x) {
	for(auto &y : v[x]) {
		long long py = y;
		if(y) y = 0, dfs(py);
	}
	cout << x << "\n"; return;
}

int main() {
	n = read(), m = read();
	for(int i = 1; i <= m; i ++) {
		long long x = read(), y = read();
		v[x].push_back(y), v[y].push_back(x);
	}
	dfs(1);
	return 0;
}

\(\text{poj-1780}\)

给定一个正整数 \(n\),求一个长度为 \(10^n + n - 1\) 的序列,满足 \(1 \sim n\) 都是序列的子串。

\(1 \le n \le 6\)


求序列的欧拉回路,由于空间限制太小了,于是只能用非递归式实现。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 1000005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long n, a[10], lt[MAXN], st[MAXN], s, t;
char ans[MAXN];

void dfs(long long res, long long maxn) {
	long long mk = 0;
	while(lt[res] < 10) {
		mk = res * 10 + lt[res];
		lt[res] ++, st[s ++] = mk;
		res = mk % maxn;
	}
	return;
}

int main() {
	a[0] = 1;
	for(int i = 1; i <= 6; i ++) a[i] = a[i - 1] * 10;
	while(n = read()) {
		if(n == 1) { cout << "0123456789\n"; continue; }
		long long maxn = a[n - 1], res = 0;
		memset(lt, 0, sizeof lt); 
		s = t = 0, dfs(0, maxn);
		while(s) {
			res = st[-- s];
			ans[t ++] = res % 10 + '0';
			res /= 10, dfs(res, maxn);
		}
		for(int i = 1; i < n; i ++) cout << "0";
		while(t) cout << ans[-- t];
		cout << "\n";
	}
	return 0;
}

\(\text{hdu-5883}\)

给定 \(n\) 个点 \(m\) 条边的无向图,点有点权,找出一条路径 \((p_1, p_2, \dots, p_k)\)

使得每条边只经过一次,求 \((a_{p_1} \oplus a_{p_2} \oplus \dots \oplus a_{p_k})_{\max}\) 的值。

\(1 \le n \le 10^5\)\(1 \le m \le 5 \times 10^5\)\(0 \le a_i \le 10^4\)


首先,用并查集判断一下图的连通性。

通过每个点的度数判断是否是欧拉图,若是判断存在欧拉回路还是欧拉路径。

对于欧拉路径,那么起点和终点被异或的次数就是 \(\frac{a_i+1}{2}\),其他的则是 \(\frac{a_i}{2}\)

对于欧拉回路,那么起点和终点不是固定的,就有多种走法,而每种走法只有起点多访问了一次。所以说对于其他点来说,被异或的次数还是 \(\frac{a_i}{2}\),对于起点来说是 \(\frac{a_i}{2} + 1\)

这样只要枚举起点取最大值就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 100005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long T, n, m, a[MAXN], d[MAXN], fa[MAXN];

long long find(long long x) { return (fa[x] == x) ? x : fa[x] = find(fa[x]); }

int main() {
	T = read();
	while(T --) {
		n = read(), m = read(); 
		long long fg = 0, cnt = 0;
		for(int i = 1; i <= n; i ++) 
			a[i] = read(), fa[i] = i, d[i] = 0;
		for(int i = 1; i <= m; i ++) {
			long long x = read(), y = read();
			d[x] ++, d[y] ++, fa[find(y)] = find(x);
		}
		for(int i = 2; i <= n; i ++) if(find(fa[1]) != find(fa[i])) fg = 1;
		for(int i = 1; i <= n; i ++) if(d[i] % 2) cnt ++;
		if(cnt != 0 && cnt != 2 || fg) { cout << "Impossible\n"; continue; }
		long long res = 0, ans = 0;
		for(int i = 1; i <= n; i ++) if((d[i] + 1) >> 1 & 1) res ^= a[i];
		if(!cnt) for(int i = 1; i <= n; i ++) ans = max(ans, res ^ a[i]);
		else ans = res;
		cout << ans << "\n";
	}
	return 0;
}

\(\text{loj-10105}\)

给定一张图,如果不可以一笔画,输出一行 NO

否则,输出一行 YES,接下来一行输出一组方案。

  1. 如果 \(t = 1\),输出 \(m\) 个整数 \(p_1, p_2, \dots, p_m\)。令 \(e = \lvert p_i \rvert\),那么 \(e\) 表示经过的第 \(i\) 条边的编号。如果 \(p_i\) 为正数表示从 \(v_e\) 走到 \(u_e\),否则表示从 \(u_e\) 走到 \(v_e\)

  2. 如果 \(t = 2\),输出 \(m\) 个整数 \(p_1, p_2, \dots, p_m\)。其中 \(p_i\) 表示经过的第 \(i\) 条边的编号。

\(1 \le n \le 10^5\)\(0 \le m \le 2 \times 10^5\)


模板题。

#include<iostream>
#include<cstdio>
#include<stack>
using namespace std;
#define MAXN 200005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long t, n, m, hd[MAXN], in[MAXN], out[MAXN], tot;
struct node { long long to, nxt, w; } e[MAXN << 1];
stack<long long> ans;
bool vis[MAXN];

void add(long long x, long long y, long long w) {
	e[tot].to = y, e[tot].nxt = hd[x], e[tot].w = w;
	hd[x] = tot ++; return;
}

void dfs(long long x) {
	for(long long &i = hd[x]; i; i = e[i].nxt) {
		node p = e[i];
		if(!vis[i >> 1]) vis[i >> 1] = 1, dfs(p.to), ans.push(p.w);
	} 
	return;
}

int main() {
	t = read(), n = read(), m = read(), tot = 2;
	for(int i = 1; i <= m; i ++) {
		long long x = read(), y = read();
		add(x, y, i), in[y] ++, out[x] ++;
		if(t == 1) add(y, x, -i);
		else tot ++;
	}
	bool fg = 1;
	if(t == 1) {
		for(int i = 1; i <= n; i ++) 
		if((in[i] + out[i]) % 2) { fg = 0; break; }
	}
	else for(int i = 1; i <= n; i ++) 
		if(in[i] != out[i]) { fg = 0; break; }
	if(!fg) { cout << "NO\n"; return 0; }
	for(int i = 1; i <= n; i ++) if(hd[i]) { dfs(i); break; }
	if(ans.size() != m) { cout << "NO\n"; return 0; }
	cout << "YES\n";
	while(!ans.empty()) { cout << ans.top() << " "; ans.pop(); }
	cout << "\n"; 
	return 0;
}

\(\text{loj-10106}\)

给定 \(n\) 个字符串,当且仅当两个字符串首尾有相同字母时,相同的首和尾可拼接。

判断这 \(n\) 个字符串是否可以拼接成一个长字符串。

\(1 \le T \le 10^5\)\(|S| \le 1000\)


把一个单词的首尾建有向边。

若形成的图存在欧拉路径或欧拉回路则有解,否则无解。

注意:需要先判联通性。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 100005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long T, n, in[MAXN], out[MAXN], fa[30];
char s[MAXN];

long long find(long long x) { return (fa[x] == x) ? x : fa[x] = find(fa[x]); }

int main() {
	T = read();
	while(T --) {
		n = read(); long long c1 = 0, c2 = 0;
		for(int i = 0; i < 26; i ++) 
			in[i] = out[i] = 0, fa[i] = i;
		for(int i = 1; i <= n; i ++) {
			cin >> (s + 1); 
			long long x = s[1] - 'a', y = s[strlen(s + 1)] - 'a';
			out[x] ++, in[y] ++, fa[find(y)] = find(x);
		}
		for(int i = 0; i < 26; i ++)
			if((in[i] || out[i]) && i == find(fa[i])) c1 ++;
		if(c1 > 1) { cout << "The door cannot be opened.\n"; continue; }
		else c1 = 0;
		for(int i = 0; i < 26; i ++)
			if(in[i] == out[i] + 1) c1 ++;
			else if(in[i] == out[i] - 1) c2 ++;
			else if(in[i] != out[i]) { c1 = -1; break; }
		if(c1 == -1 || c1 != c2) cout << "The door cannot be opened.\n";
		else if(c1 == 0 || c1 == 1) cout << "Ordering is possible.\n";
		else cout << "The door cannot be opened.\n";
	}
	return 0;
}

\(\text{loj-10108}\)

给定 \(n\) 个点 \(m\) 条边的无向图,求至少需要几笔把所有边都画一遍。

\(1 \le n \le 10^5\)\(0 \le m \le 2 \times 10^5\)\(1 \le a,b \le n\)


显然对于每个连通块,答案就是奇数点的一半,但对于没有奇数点情况答案为 \(1\)

并查集维护连通块即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 100005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long n, m, d[MAXN], fa[MAXN], cnt[MAXN];

long long find(long long x) { return (fa[x] == x) ? x : fa[x] = find(fa[x]); }

int main() {
	while(cin >> n >> m) {
		for(int i = 1; i <= n; i ++) 
			d[i] = cnt[i] = 0, fa[i] = i;
		for(int i = 1; i <= m; i ++) {
			long long x = read(), y = read();
			d[x] ++, d[y] ++, fa[find(y)] = find(x);
		}
		long long ans = 0;
		for(int i = 1; i <= n; i ++) cnt[find(i)] += d[i] % 2;
		for(int i = 1; i <= n; i ++) if(fa[i] == i) 
			if(d[i] && !cnt[i]) ans ++;
			else ans += cnt[i] / 2;
		cout << ans << "\n";
	}
	return 0;
}

\(\text{bzoj-3033}\)

\(2^n\) 个字符串,分别是 \(0 \sim 2^n - 1\) 的二进制串,若两个字符串首尾有公共子串则可以把重叠部分拼接起来,求这 \(2^n\) 个字符串最短的拼接形式,若有多种拼接方式输出字典序最小的。

\(2 \le n \le 11\)


考虑构造,最朴素的想法无非就是暴力枚举字符串形态然后 \(\text{check}\)

但复杂度不是 \(2^n\) 而是 \(2^{2^n}\) 的。

因为要求字典序最小,这个串的前 \(n\) 位一定为 \(0\),可以证明这样一定有解。

我们发现从第二位开始,每个数只可能是在上一个数的后 \(n−1\) 位后面补一个 \(0\)\(1\) 形成的。

那么直接暴搜下一个数的情况。由于每条边最多只会被遍历一次所以神奇的不会 \(\text{TLE}\)

#include<iostream>
#include<cstdio>
using namespace std;
#define MAXN 100005

long long read() {
	long long x = 0, f = 1;
	char c = getchar();
	while(c > 57 || c < 48) { if(c == 45) f = -1; c = getchar(); }
	while(c >= 48 && c <= 57) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
	return x * f;
}

long long k, n, ans[MAXN];
bool vis[MAXN];

bool solve(long long x, long long p) {
	if(vis[x]) return 0;
	vis[x] = 1, ans[p] = x & 1;
	if(p == n) return 1;
	if(solve((x << 1) & (n - 1), p + 1)) return 1;
	if(solve((x << 1 | 1) & (n - 1), p + 1)) return 1;
	vis[x] = 0; return 0;
}

int main() {
	k = read(), n = 1 << k;
	cout << n << " ";
	for(int i = 1; i < k; i ++) cout << "0";
	solve(0, 1);
	for(int i = 1; i <= n - k + 1; i ++) cout << ans[i];
	cout << "\n";
	return 0;
}
posted @ 2025-12-19 21:59  So_noSlack  阅读(4)  评论(0)    收藏  举报