BZOJ 2115 Xor(线性基)
题意:给定一个n<=50000个点m<=100000条边的无向联通图,每条边上有一个权值wi<=1e18。请你求一条从1到n的路径,使得路径上的边的异或和最大.
任意一条1到n的路径的异或和,都可以由任意一条1到n路径的异或和与图中的一些环的异或和来组合得到。
为什么?假如我们已经有一条1到n的路径,考虑在出发之前,先走到图中任意一个环上面,走一遍这个环,然后原路返回,这样我们既得到了这个环的异或值(走到环的路径被走过了 2 次,抵消了),也返回了点1。我们可以对任意的环这样做,从而获得这个环的异或值。有了这个性质,不难验证上述结论是正确的。
现在的解题思路就非常明确了,首先找出所有的环(利用 DFS 树中的返祖边来找环),然后找一条任意的1到n的路径,其异或值为ans。则我们就需要选择若干个环,使得这些这些环上的异或值与ans异或起来最大。这就转化为线性基的问题了。
求出所有环的异或值的线性基,由于线性基的良好性质,只需要从大到小考虑选择每个线性基向量能否使得异或值更大即可,容易用贪心证明正确性。
# include <cstdio> # include <cstring> # include <cstdlib> # include <iostream> # include <vector> # include <queue> # include <stack> # include <map> # include <set> # include <cmath> # include <algorithm> using namespace std; # define lowbit(x) ((x)&(-x)) # define pi acos(-1.0) # define eps 1e-3 # define MOD 1000000007 # define INF 1000000000 # define mem(a,b) memset(a,b,sizeof(a)) # define FOR(i,a,n) for(int i=a; i<=n; ++i) # define FO(i,a,n) for(int i=a; i<n; ++i) # define bug puts("H"); # define lch p<<1,l,mid # define rch p<<1|1,mid+1,r # define mp make_pair # define pb push_back typedef pair<int,int> PII; typedef vector<int> VI; # pragma comment(linker, "/STACK:1024000000,1024000000") typedef long long LL; int Scan() { int res=0, flag=0; char ch; if((ch=getchar())=='-') flag=1; else if(ch>='0'&&ch<='9') res=ch-'0'; while((ch=getchar())>='0'&&ch<='9') res=res*10+(ch-'0'); return flag?-res:res; } void Out(int a) { if(a<0) {putchar('-'); a=-a;} if(a>=10) Out(a/10); putchar(a%10+'0'); } const int N=50005; //Code begin... struct Edge{int p, next; LL w;}edge[N<<2]; int head[N], cnt=1, vis[N], num; LL p[63], node[N], a[N*5]; void add_edge(int u, int v, LL w) { edge[cnt].p=v; edge[cnt].next=head[u]; edge[cnt].w=w; head[u]=cnt++; } void dfs(int x, int fa) { vis[x]=1; for (int i=head[x]; i; i=edge[i].next) { int v=edge[i].p; if (v==fa) continue; if (vis[v]) a[++num]=node[v]^node[x]^edge[i].w; else node[v]=node[x]^edge[i].w, dfs(v,x); } } void sol() { FOR(i,1,num) { for (int j=62; j>=0; --j) { if (!(a[i]>>j)) continue; if (!p[j]){p[j]=a[i]; break;} a[i]^=p[j]; } } } int main () { int n, m, u, v; LL w; scanf("%d%d",&n,&m); while (m--) scanf("%d%d%lld",&u,&v,&w), add_edge(u,v,w), add_edge(v,u,w); dfs(1,0); sol(); LL ans=node[n]; for (int i=62; i>=0; --i) if ((ans^p[i])>ans) ans^=p[i]; printf("%lld\n",ans); return 0; }