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;
}