描述
题解
给定一个无向图,关于欧拉路的问题。这里我们要求的是这个路径的结点异或在一起的最大值。
首先,我们可以通过判断度的奇偶性来判断是否是 Impossible ,因为欧拉路径存在两种,一种是欧拉通路,一种是欧拉回路。通路是起点、终点是不同点,那么我们需要将起点终点多算一次,并且起点和终点是固定的,必然是度为奇数的结点,那么我们也就知道了这种情况需要度为奇数的结点只有连个;回路呢,其实是起点、终点重合,变成了同一个点,所以呢,我们需要对这个起点多算一次即可了,但是这里起点不唯一,所以需要枚举起点。
那么问题来了,每个结点的异或次数是多少呢?实际上这个和度有关,因为我们可以想象的到的是,每次经过一个结点,除了起点和终点外,必然是一个入度和一个出度,想要让每条路都走一遍,需要把这些度都消费掉,那么每个结点的异或次数自然就是度数的一半了,然而这里还要一个小小的优化就是,异或不管次数再多,只有两种结果,一种是异或奇数次,一种是异或偶数次,因为偶数次的异或会抵消异或的效力,酱紫就好做很多了。这里需要注意的是,起点和终点多算一次异或也就行了。
所以呢,这里的第一个过程不考虑起点时,求得一个异或值后,接着在第二个过程加入起点和终点,如果是通路,就是固定起点和终点,比较容易解决,回路就需要遍历求最大值,而这里我们需要注意的是,插入起点后,结果可能比第一个过程的临时结果大,也可能小,所以需要格外注意取最的过程,不要以为只会变大哦~~~
对了,网上看到了一种别人的 AC 代码,十分的古怪,经过仔细的研究,发现他的代码可能是因为测试数据比较弱导致 AC 的,测试数据应该叫上一组酱紫的:
1
6 10
1 2 4 8 16 32
1 6
1 5
1 4
1 2
2 5
2 4
2 3
3 4
4 5
5 6
// ans = 52
而他的代码结果竟然为 63 ,明显是重复计算了起点,他的代码问题出在了回路枚举起点的过程,脑洞回路有些怪,所以写了那么个稀奇的代码,让我推敲了好大会儿, hack 了一组数据搞搞事情~~~额,说起来 hack 这个词,我也是刚刚才知道是什么意思的。具体是哪个朋友的题解我就不说了,如果看到了那个错的 AC 代码,您可以看到我在评论区的一番疑问……O(∩_∩)O哈哈~
这是我第一次做欧拉路的问题,感觉花费了好几个小时是值得的,让我更深入的理解了欧拉路的存在。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 1e5 + 7;
int n, m;
int a[MAXN];
int degree[MAXN];
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
memset(degree, 0, sizeof(degree));
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
int u, v;
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &u, &v);
degree[u]++;
degree[v]++;
}
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (degree[i] & 1)
{
cnt++;
}
}
if (cnt != 0 && cnt != 2)
{
printf("Impossible\n");
continue;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
degree[i] = (degree[i] + 1) >> 1;
if (degree[i] & 1)
{
ans ^= a[i];
}
}
int tmp = 0;
if (cnt == 0)
{
for (int i = 1; i <= n; i++)
{
tmp = max(tmp, ans ^ a[i]);
}
ans = tmp;
}
printf("%d\n", ans);
}
return 0;
}