B. 咕咕

题链

Description

按位与最长路。

Analysis

看到与运算,会想到按位操作从而得到最大的与结果。

考虑若 \(a>b\),如果可以得到 \(2^a\),则一定不会通过舍弃 \(2^a\) 得到 \(2^b\)。那么我们可以从高位开始枚举,一一排查二进制中各位能否取到贪心地,则按降序能取尽取,取后累计(切勿取低位丢高位)。根据刚刚的论证,那么这个贪心思路显然正确。

下面讨论如何判断能否取到。

我们并非在读入时就建边,为了方便多次检验不同二进制位,我们可以先将所有的边存起来。

对于每一次判断:要达到 \(x\) 这个与结果,显然我们走过的边权 \(w\) 只可能满足 \((x\) \(\text{and}\) \(w)\geq x\)。故建图时只需加入这些边权满足条件的边即可。然后从 \(1\) 开始 BFS,看看通过这些满足条件的边能否到达 \(n\),若能到达则该状态可行。

最后注意每次建图前要清空

Code

#include <stdio.h>
#include <queue>
#include <string.h>
#define int long long
inline int read() {
	int x = 0; bool w = 1; char c = getchar();
	for (; c < '0' || c > '9'; c = getchar()) if (c == '-') w ^= 1;
	for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	return w ? x : -x;
}
const int N = (int)1e5 + 5, M = (int)5e5 + 5;
int hd[N], eL = 1;
struct Edge {
	int to, w, nxt;
} e[M << 1];
struct Edg {
	int u, v, w;
	inline void in() {
		u = read(), v = read(), w = read();
	}
} ed[M];
inline void AE(int fr, int to, int w) {
	e[++eL] = (Edge){to, w, hd[fr]};
	hd[fr] = eL;
}
int n, m; bool vis[N];
std:: queue<int> node;
inline bool Bfs(int key) {
	memset(hd, 0, sizeof hd); eL = 1; //记得初始化
	for (int i = 1; i <= m; ++i)
		if ((ed[i].w & key) == key) //建符合条件的边
			AE(ed[i].u, ed[i].v, ed[i].w), AE(ed[i].v, ed[i].u, ed[i].w);
	memset(vis, false, sizeof vis);
	vis[1] = true; node.push(1);
	while (!node.empty()) { //BFS
		int u = node.front(); node.pop();
		for (int i = hd[u]; i; i = e[i].nxt)
			if (!vis[e[i].to]) vis[e[i].to] = true, node.push(e[i].to);
	}
	return vis[n];
}
signed main(void) {
	freopen("gugu.in", "r", stdin); freopen("gugu.out", "w", stdout);
	n = read(), m = read();
	for (int i = 1, u, v, w; i <= m; ++i) ed[i].in();
	int ans = 0;
	for (int i = 63; i >= 0; --i)
		if (Bfs(ans + (1ll << i))) ans |= 1ll << i; //判断 + 累计
	printf("%lld\n", ans);
	return 0;
}

Postscript

听说还有二分答案做法。

(拇指点点

posted @ 2022-11-19 15:08  Dry_ice  阅读(64)  评论(0)    收藏  举报