【图论,数学 + 线性基】AcWing 228. 异或

分析

思路很有价值的一道题。

首先我们可以注意到,一种解的形式一定是一条 \(1\to n\) 的路径以及若干个环(当然可以是 \(0\) 个)。

因此下面只需要证明两个结论:

  1. 一条 \(1\to n\) 的路径(的异或值)一定能够被另一条 \(1\to n\)​ 的路径以及若干个环拼凑出来。
  2. 任意一个能够被起点到达的环(的异或值)一定能被选取。

证明

  • 对于第一个结论:可以发现如果 \(1\to n\)​ 存在两条路径,那么这两条路径之间一定能形成一个环,那么我们一定能从一条路径 + 环异或得到另一条路径。
  • 第二个结论比较明显,如果要选那个环,就从起点出发到那个环上绕一圈再回到起点即可。

有了上面两个结论,我们就能够保证这个做法正确性:任意选取一条 \(1\to n\)​ 的路径的异或值,然后将其异或上若干个环(下面说具体做法)并得到最优解(也就是所需要求的解)。

具体做法:

以下图为例:
image

我们从 \(1\)​​ 出发做一遍 \(\texttt{dfs}\)​​,然后将第一次访问的到的点所对应的边记为树边,否则记为环边

例如 \(\texttt{dfs}\) 访问的过程中经过点的顺序为 \(1 \to 2 \to 3\to 4 \to 5\),那么树边(绿色)和环边(红色)如下图所示:

image

当我们得到环边的时候,将相应的环加入一个集合 \(ring\) 中,比如下图标出来的环边(黄色的边)对应的环(橙色标注)就是:
image

可以发现,这样能够保证所有环能够被直接或间接地异或得到,也就保证了正确性。(例如环 \(\{ 2, 3, 5, 4\}\) 可以被 \(\{ 2, 3, 4\}, \{ 3, 4, 5\}\) 异或得到)

最后的任务是从集合 \(ring\)​ 中选若干个以及任意一条初始路径进行异或使得答案最大,这里可以使用线性基来解决。

实现

// Problem: 异或
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/230/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;

#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()

#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;

#define int long long

inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=1e5+5, M=N<<1;

struct Edge{
	int to, w, next;
}e[M];

int h[N], tot;

void add(int u, int v, int w){
	e[tot].to=v, e[tot].w=w, e[tot].next=h[u], h[u]=tot++;
}

int n, m;
vector<int> ring;
bool vis[N];
int d[N];
void dfs(int u){
	vis[u]=1;
	for(int i=h[u]; ~i; i=e[i].next){
		int go=e[i].to;
		if(!vis[go]) d[go]=d[u]^e[i].w, dfs(go);
		else ring.pb(d[go]^d[u]^e[i].w);
	}
}

signed main(){
	memset(h, -1, sizeof h);	
	cin>>n>>m;
	rep(i,1,m){
		int u, v, w; read(u), read(v), read(w);
		add(u, v, w), add(v, u, w);
	}
	
	dfs(1);
	int res=d[n];
	vector<int> b;
	
	for(auto i: ring){
		int val=i;
		for(auto j: b) val=min(val, j^val);
		if(val) b.pb(val);
	}
	
	for(auto i: b) res=max(res, res^i);
	
	cout<<res<<endl;
	
	return 0;
}
posted @ 2022-02-14 23:12  HinanawiTenshi  阅读(57)  评论(0编辑  收藏  举报