洛谷·[WC2011]最大XOR和路径

初见安~这里是传送门:洛谷P4151 最大XOR路径洛谷·[WC2011]最大XOR和路径_第1张图片

题解:

题意很简单,求一无向图从1到n的路径的最大异或和,可以重复走边和点。

众所周知,无向图找路径,遇事不决找连通分量或者环【什么东西啊!但有的时候真的可以撞对的】。这个题的话连通分量似乎跟路径关系不大,所以我们找环吧。环有个很优美的性质就是可以走一圈后回到原点,继续走别的路径,并且会获得整个环上的边权的异或和。

我们假定当前已有链路径1~n,外面有一个环:

洛谷·[WC2011]最大XOR和路径_第2张图片

如果我们选择去走环,去环里绕一圈再回来,那么x边和y边分别走了两次,异或起来贡献为0。换言之,我们不需要考虑从路径连接到环的边

所以找一条从1到n的路径,然后看再去走哪些环最优就可以了。后半句我们可以预处理所有环的异或和,扔进线性基【线性基基本操作】,前半句的话其实随便找一条路径就可以了。因为假设存在两条从1到n的链路径,那么一定会形成一个大环。这时候如果你选的不是最优的那一条路,那么异或上大环过后就相当于走最优的那条路了。

好了上代码——

#include
#include
#include
#include
#include
#include
#define maxn 50005
#define maxm 100005
//#define int long long
using namespace std;
typedef long long ll;
ll read() {
	ll x = 0, f = 1, ch = getchar();
	while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
	while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
	return x * f;
}

struct edge {ll to, w, nxt;} e[maxm << 1];
int head[maxn], k = 0;
void add(ll u, ll v, ll w) {e[k] = {v, w, head[u]}; head[u] = k++;}

int n, m, tot = 0, dfn[maxn];
bool vis[maxn];
ll p[maxn], dis[maxn], rand_dis;
void insert(ll x) {//加入线性基
	for(ll i = 63; i >= 0; i--) if(x >> i) {
		if(!p[i]) {p[i] = x; return;}
		x ^= p[i];
	}
}

void dfs(int u) {
	vis[u] = true; dfn[u] = ++tot;//dfn是时间戳,可以用来小小优化一下避免一个环被多次找到
	for(int i = head[u], v; ~i; i = e[i].nxt) {
		v = e[i].to; 
		if(!vis[v]) dis[v] = dis[u] ^ e[i].w, dfs(v);
		else if(dfn[u] > dfn[v]) insert(dis[u] ^ e[i].w ^ dis[v]);
	}
}

signed main() {
	memset(head, -1, sizeof head);
	n = read(), m = read();
	for(ll u, v, w, i = 1; i <= m; i++) u = read(), v = read(), w = read(), add(u, v, w), add(v, u, w);
	dfs(1); rand_dis = dis[n];
	for(int i = 63; i >= 0; i--) rand_dis = max(rand_dis, rand_dis ^ p[i]);//直接找最大
	printf("%lld\n", rand_dis);
	return 0;
}

关于里面的那个dfn,其实手枚可以发现一个环可能被多次扔进去的。所以加一个dfn可以稍稍优化一点,总时间快了100ms的样子。

迎评:)
——End——

你可能感兴趣的:(线性基)