2115: [Wc2011] Xor
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 4762 Solved: 1985
[ Submit][ Status][ Discuss]
Description
Input
第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目。 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 Di的无向边。 图中可能有重边或自环。
Output
仅包含一个整数,表示最大的XOR和(十进制结果),注意输出后加换行回车。
Sample Input
1 2 2
1 3 2
2 4 1
2 5 1
4 5 3
5 3 4
4 3 2
Sample Output
HINT
Source
题意:
给定一个 n(n≤50000) 个点 m(m≤10000) 条边的无向图,
每条边上有一个权值。请你求一条从 1 到 n 的路径,使得路径上的边的异或和最大。
题解:
这道题要求从1到n的最大XOR的路径,存在重边,允许经过重复点重复边
那么在图上作图尝试之后就会发现,路径一定是由许多的环和一条从1到n的路径组成。容易发现,来回走是没有任何意义的,因为来回走意味着抵消。考虑这道题求得是路径xor和最大,所以必然我们要想办法处理环的情况。
任意地先找出一条从1到n的路径,把这条路径上的xor和作为ans初值(先不管为什么可行),然后我们的任务就变成了求若干个环与这个ans初值所能组合成的xor最大值。显然,我们需要预处理出图上所有的环,并处理出所有环的环上xor值,这当然是dfs寻找,到n的路径的时候顺便求一下就可以了。
任意地先找出一条从1到n的路径,把这条路径上的xor和作为ans初值(先不管为什么可行),然后我们的任务就变成了求若干个环与这个ans初值所能组合成的xor最大值。显然,我们需要预处理出图上所有的环,并处理出所有环的环上xor值,这当然是dfs寻找,到n的路径的时候顺便求一下就可以了。
现在我们来讨论上述做法的可行性。
第一种情况:我们对最终答案产生贡献的某个环离1到n的主路径很远,这样的话,因为至少可以保证1可以到达这个环,那么我们可以走到这个环之后绕环一周之后原路返回,这样从1走到环的路上这一段被重复经过所以无效,但是环上的xor值被我们得到了,所以我们并不关心这个环和主路径的关系,我们只关心环的权值。
第二种情况:我们任意选取的到n的路径是否能保证最优性。假设存在一条更优的路径从1到n,那么这条路径与我们原来的路径构成了一个环,也就会被纳入线性基中,也会被计算贡献,假如这个环会被经过,那么最后的情况相当于是走了两遍原来选取的路径,抵消之后走了一次这个最优路径,所以我们无论选取的是哪条路径作为ans初值,都可以通过与更优情况构成环,然后得到一样的结果。这一证明可以拓展到路径上的任意点的路径选取。
转自:https://www.cnblogs.com/ljh2000-jump/p/5869925.html
代码:
/** 给定一个 n(n≤50000) 个点 m(m≤10000) 条边的无向图, 每条边上有一个权值。请你求一条从 1 到 n 的路径,使得路径上的边的异或和最大。 */ #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 5e4+10; const int maxm = 1e6+10; typedef struct node { int to,next; ll val; node(){}; node(int t,int ne,ll v):to(t),next(ne),val(v){}; }Edge; Edge edge[maxm]; int head[maxn],tot,cnt; bool vis[maxn]; ll circle[maxm],dist[maxn]; int readi() { char ch = getchar();int t = 0; while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') t = (t<<3)+(t<<1)+ch-48,ch = getchar(); return t; } ll readl() { char ch = getchar(); ll t = 0; while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') t = (t<<3)+(t<<1)+ch-48,ch = getchar(); return t; } void addedge(ll val,int v,int u) { //printf("%d %d %lld\n",u,v,val); edge[++tot] = (Edge){v,head[u],val};head[u]=tot; edge[++tot] = (Edge){u,head[v],val};head[v]=tot; } void init() { memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); memset(dist,0,sizeof(dist)); memset(circle,0,sizeof(circle)); cnt = tot = 0; } void dfs(int u) { vis[u] = 1; for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(!vis[v]) { dist[v] = dist[u]^edge[i].val; dfs(v); } else circle[++cnt] = dist[u] ^ dist[v] ^ edge[i].val; } } const int MaxBasis = 63; struct LB { ll base[MaxBasis+10]; bool rel; int sz; vector<ll> Basis; LB() { memset(base, 0, sizeof(base)); rel = false; sz = 0; Basis.clear();} void init() { memset(base,0,sizeof(base));rel = false;sz = 0;Basis.clear(); } bool add(ll x) { ///加入线性基中 for (int i = MaxBasis; i >= 0; --i) { if (!(x >> i & 1)) continue; if (base[i]) x ^= base[i]; else { for (int j = 0; j < i; ++j) if (x >> j & 1) x ^= base[j]; for (int j = i+1; j <= MaxBasis; ++j) if (base[j] >> i & 1) base[j] ^= x; base[i] = x, ++sz; return true; } } rel = true; /// 表示有一个没插入进去,则说明当前线性基能表示出x,则如果放入x存在异或为0的结果。 return false; } ll Max(ll ans = 0) { ///取最大 for(int i=0;i<=MaxBasis;i++) ans = max(ans,ans^base[i]); return ans; } ll Min(ll ans = 0) { ///取最小 for(int i=0;i<=MaxBasis;i++) ans = min(ans,ans^base[i]); return ans; } void GetBasis() { ///构造向量,用于之后求第k小 for (int i = 0; i <= MaxBasis; ++i) if (base[i]) Basis.push_back(base[i]); } void umerge(struct LB &b) { ///合并线性基 for(int i=0;i<=MaxBasis;i++) if(b.base[i]) add(b.base[i]); } ll Min_Kth(ll k) { ///线性基中第k小 if(rel) k--; ///线性基未满存在0 if(k >= ((ll)1<<sz)) return -1; ll ans = 0; for(int i=0;i<sz;i++) if(k & ((ll)1<<i)){ ans ^= Basis[i]; } return ans; } }lb; int main() { int n,m; while(~scanf("%d%d",&n,&m)) { init(); for(int i=1;i<=m;i++) { addedge(readl(),readi(),readi()); } dfs(1); lb.init(); ll ans = dist[n]; for(int i=1;i<=cnt;i++) lb.add(circle[i]); printf("%lld\n",lb.Max(ans)); } return 0; }