http://www.lydsy.com/JudgeOnline/problem.php?id=2115
题意:给出一个n个点m条边的无向连通边加权图,求1~n的某条路径使得异或值最大(可以重复点可以重复边)(n<=50000, m<=100000)
#include <bits/stdc++.h> using namespace std; const int N=50005, M=100015; typedef long long ll; struct E { int next, to; ll w; }e[M<<1]; int cnt, ihead[N], tot, n, m; ll d[N], q[M*10], a[65]; void add(int u, int v, ll c) { e[++cnt]=(E){ihead[u], v, c}; ihead[u]=cnt; e[++cnt]=(E){ihead[v], u, c}; ihead[v]=cnt; } bool vis[N]; void dfs(int x) { vis[x]=1; for(int i=ihead[x]; i; i=e[i].next) if(!vis[e[i].to]) d[e[i].to]=d[x]^e[i].w, dfs(e[i].to); else q[++tot]=d[e[i].to]^d[x]^e[i].w; } int main() { scanf("%d%d", &n, &m); for(int i=0; i<m; ++i) { int x, y; ll w; scanf("%d%d%lld", &x, &y, &w); add(x, y, w); } dfs(1); for(int i=1; i<=tot; ++i) for(int j=60; j>=0; --j) if((q[i]>>j)&1) { if(!a[j]) { a[j]=q[i]; break; } else q[i]^=a[j]; } ll ans=d[n]; for(int j=60; j>=0; --j) if(!((ans>>j)&1)) ans^=a[j]; printf("%lld\n", ans); return 0; }
好题不解释..
首先去学习了一下线性基。这里说的线性基在这里具体是指某个向量数组在xor操作下形成的封闭集合中的线性无关量。我们求出这些基后就能很简单的贪心求出答案了。
然后再学习了一个特殊的性质= =在生成树上的非树边所形成的环在xor的情况下能表示出所有环的xor值(这个yy了好久证明不出,只能手工验证正确性..大概就是每一个非树边都存在有且一个所求出的环中,然后用一些环进行xor抵消掉一些树边然后就形成了新的环)
基的个数上界就是$O(log(n))$
然后我们只需要通过高斯消元求出每一位都独立的一个元素那么可以当成一个基,最后一定会出现一组基...大小为$O(log(n)), n为向量的大小$,也就是说,某个基的最高位存在而其它基都不存在这个位。
由于xor操作的封闭性,我们只需要像高斯消元一样消去某一位上其它元素的值即可。
回到本题...
由于一条1~n的路径可以由任意一条1~n的简单路径加上任意个环组成。(从简单路径中进入环的路径和出环的路径(同一条)xor抵消)
由之前所说,非树边的环可以线性组成所有环的xor值,所以我们直接搞就行了...
(ps:诶呀诶呀一定要强拉到基吗...最好理解的就是,我要贪心,所以我要变成一些数使得最高位只有一个数有其他数没有,而且这些数能线性组合成之前的向量所能组成的所有数。就行了你们说是不是,sb iwtwiioi还扯了一大版自己都不懂的东西
时间: 2024-11-03 22:48:41