DFS + 高斯消元 HDU 5544

来源:互联网 发布:windows sdk如何安装 编辑:程序博客网 时间:2024/06/05 20:14

Ba Gua Zhen

Time Limit: 6000/4000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 874    Accepted Submission(s): 256


Problem Description
During the Three-Kingdom period, there was a general named Xun Lu who belonged to Kingdom Wu. Once his troop were chasing Bei Liu, he was stuck in the Ba Gua Zhen from Liang Zhuge. The puzzle could be considered as an undirected graph with N vertexes and M edges. Each edge in the puzzle connected two vertexes which were ui and vi with a length of wi. Liang Zhuge had great interests in the beauty of his puzzle, so there were no self-loops and between each pair of vertexes, there would be at most one edge in the puzzle. And it was also guaranteed that there was at least one path to go between each pair of vertexes.

Fortunately, there was an old man named Chengyan Huang who was willing to help Xun Lu to hack the puzzle. Chengyan told Xun Lu that he had to choose a vertex as the start point, then walk through some of the edges and return to the start point at last. During his walk, he could go through some edges any times. Since Liang Zhuge had some mysterious magic, Xun Lu could hack the puzzle if and only if he could find such a path with the maximum XOR sum of all the edges length he has passed. If the he passed some edge multiple times, the length would also be calculated by multiple times. Now, could you tell Xun Lu which is the maximum XORcircuit path in this puzzle to help him hack the puzzle?
 

Input
The first line of the input gives the number of test cases, T(1T30)T test cases follow.

Each test case begins with two integers N(2N5×104) and M(1M105) in one line. Then M lines follow. Each line contains three integers uivi and wi(1ui,viN,0wi2601) to describe all the edges in the puzzle.
 

Output
For each test case, output one line containing Case #x: y, where x is the test case number (starting from 1) and y is the maximum XOR sum of one circuit path in the puzzle.
 

Sample Input
23 31 2 11 3 22 3 06 71 2 11 3 12 3 13 4 44 5 24 6 25 6 2
 

Sample Output
Case #1: 3Case #2: 3
Hint
A XOR takes two bit patterns of equal length and performs the logical exclusive OR operation on each pair of corresponding bits. The result in each position is 1 if only the first bit is 1 or only the second bit is 1, but will be 0 if both are 0 or both are 1. In this we perform the comparison of two bits, being 1 if the two bits are different, and 0 if they are the same.
 

题意:给出一个无向图,有 n 个点和 m 条边,且任意两个点之间一定连通,每条边都有一个权值 w ,现在要求找出一个回路,要求这个回路的路径上的权值的异或值最大。

思路:首先可以想到,一个最终的回路,可以由许多小的基本回路构成,因为题目说了每条边可以多次经过,所有即使是两个不相连的回路,也可以通过另一条路过去再回来,所以只要把所有基本的小回路先找出来,然后再组合成大回路就可以了

找小回路:

首先从任意一个点出发,进行dfs,每到一个点,如果这个点没有被访问过,那么记录从出发点一直到该点的异或值,如果这个点被访问过了,那么这个点形成回路,而回路的值就是这个点的新异或值去异或旧的异或值,用一个数组dis[] 存旧异或值,用len[]存形成回路时的异或值,看下图


这个图从点1开始dfs,到点2 的时候,点2 记录的异或值 = a,然后继续深搜,当再次到达点2 的时候,新的异或值就变成了 a ^ b ^ c ^ d ^ e,旧的异或值是 a ,那么新值异或旧值就 =  b ^ c ^ d ^ e,正好就是 这个基本回路的异或值

所以形成回路时 len[cnt++] =  dis[u] ^ dis[v] ^ w; (ps: dis[u] ^ w = 新异或值,dis[v] = 旧异或值, cnt 表示回路个数)

所有的子回路都找完的时候,就得把子回路拼成大回路了,首先题目给出的每条边的权值最大是 2^60,而根据异或性质,这些值的最终异或结果是不可能超过 2^60的,所以最大的异或值就是 2^60

高斯消元:

我们要使整个图的异或值最大,那么就把它作为二进制来处理,当然是优先取能是高位 & 出来结果为 1 的了

所以我们从第60位开始,(1 << 60) & len[i],如果&出来结果位1,那么停止,记这个回路为 x 回路,这个回路要取 row ++ (row表示取数)

这个回路取完之后,再循环一遍,因为还有一些回路 & (1<<60)也是得1的,这些回路再取的时候会把这个大的值给异或掉,所以每次都得把这个回路 和 x 回路捆绑着取,这样那个大值就不会被异或掉,所以这个回路的值更新为异或x的值,就是 len[i] = len[i] ^ len[x],这样处理完后这些其他的回路就相当于变成了小回路了。

然后再去处理 (1<<59) 的,同样的作法一直处理到 1,结束之后,把这些取出来的回路的值全部异或起来,就是所得的最大的异或值了

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<vector>using namespace std;#define ll long long#define maxn 50050#define maxm 100050#define mem(a,x) memset(a,x,sizeof(a))int n,m,cnt;bool vis[maxn];ll dis[maxn],len[maxm<<1];struct node{int to;ll val;node(int _to,ll _val):to(_to),val(_val) {}};vector<node>vec[maxn];void dfs(int u,int pre){vis[u] = true;int sz = vec[u].size();for(int i = sz - 1;i >= 0;i--){int v = vec[u][i].to;ll w = vec[u][i].val;if(v == pre)continue;if(!vis[v]){dis[v] = dis[u] ^ w;dfs(v,u);}else{len[cnt++] = dis[u] ^ dis[v] ^ w;}}void init(){for(int i = 0;i <= n;i++)vec[i].clear();mem(vis,false);dis[1] = 0;cnt = 0;}int guass(){int row = 0;for(int i = 60;i >= 0;i--){int j;for(j = row;j < cnt;j++){if(len[j] & (1ll << i)) //找到一个高位异或为1的回路 break;}if(j != cnt){swap(len[j],len[row]);//把该回路放前面,表示要取 for(j = 0;j < cnt;j++){ if(j == row)continue;if(len[j] & (1ll << i)) //把剩下的高位异或为1的回路更新为小回路 len[j] ^= len[row];}row++;}}return row;}int main(){int T,Case = 1;int u,v;ll w;scanf("%d",&T);while(T--){scanf("%d %d",&n,&m);init();for(int i = 1;i <= m;i++){scanf("%d %d %lld",&u,&v,&w);vec[u].push_back(node(v,w));vec[v].push_back(node(u,w));}dfs(1,-1);int row = guass();ll ans = 0;for(int i = 0;i < row;i++){ans ^= len[i];}printf("Case #%d: %lld\n",Case++,ans);}return 0;}


原创粉丝点击