Not Another Path Query Problem
Not Another Path Query Problem
图层拆分(路径长度,与运算)
题意简述
给定一张 \(n\) 个点、\(m\) 条无向边的图,边权为 \(w_i\)(\(0 \le w_i < 2^{60}\))。
定义一条路径的价值为路径上所有边权的按位与(bitwise AND)。
给定阈值 \(V\),每次询问两点 \(u, v\) 之间是否存在一条价值至少为 \(V\) 的路径。
核心观察
路径的价值 \(X\) 是路径上所有边权的按位与结果。
我们需要判断是否存在路径使得 \(X \ge V\)。
按位比较两个数 \(X\) 和 \(V\) 时,从最高位开始,找到第一个不同的位:
- 如果该位上 \(X=1\) 而 \(V=0\),则 \(X > V\);
- 如果该位上 \(X=0\) 而 \(V=1\),则 \(X < V\);
- 如果所有位都相同,则 \(X = V\)。
因此,\(X \ge V\) 可以拆解为以下两类情况:
情况 1:\(X\) 包含 \(V\) 的所有 1 位
即对于所有满足 \(V\) 的第 \(i\) 位为 1 的位,\(X\) 的第 \(i\) 位也必须为 1。
此时 \(X\) 至少等于 \(V\)(可能在某些 \(V=0\) 的位上 \(X=1\),那就更大)。
这等价于要求路径上所有边权都包含 \(V\) 的所有 1 位,即:
我们记这个掩码为 \(mask = V\)。
情况 2:存在某个 \(V\) 为 0 的位 \(i\),使得 \(X\) 在该位为 1,且所有高于 \(i\) 的、\(V\) 为 1 的位,\(X\) 也为 1
这样从高位比较时,第一个不同的位就是第 \(i\) 位(\(V=0, X=1\)),所以 \(X > V\)。
低于 \(i\) 的位任意。
对于每个 \(V\) 中为 0 的位 \(i\),我们构造一个掩码:
然后只保留满足 \((w \& mask) == mask\) 的边,判断询问点对是否连通。
注意:不需要枚举低于最低位 1 的那些 0 位。因为如果 \(X\) 在这些位上为 1,并且高于该位的 \(V\) 的 1 位也都为 1,那么实际上 \(X\) 已经包含了 \(V\) 的所有 1 位,属于情况 1,会被 \(mask = V\) 检查到。
算法步骤
- 读入所有边和询问。
- 构造所有需要的掩码
mask:- 首先加入
mask = V。 - 找到 \(V\) 的最低为 1 的位
lowbit。 - 枚举 \(i\) 从 61 到
lowbit + 1:- 如果 \(V\) 的第 \(i\) 位为 1,跳过;
- 否则,构造
mask:\[mask = ((1 \ll 62) - 1) - ((1 \ll i) - 1) \]然后mask = mask & V,再mask |= (1 \ll i)。 - 加入
mask列表。
- 去重(可选)。
- 首先加入
- 对于每个
mask:- 初始化并查集。
- 遍历所有边,如果
(w & mask) == mask,则合并边的两个端点。 - 遍历所有尚未确定答案的询问,如果两点在同一个连通块中,则该询问答案为
Yes,并标记为已确定。
- 输出所有询问的答案。
正确性证明
- 如果存在一条路径,其按位与结果 \(X \ge V\),那么根据按位比较规则,要么 \(X\) 包含 \(V\) 的所有 1 位(情况 1),要么存在某个 \(V=0\) 的位 \(i\) 使得 \(X\) 在第 \(i\) 位为 1 且高于 \(i\) 的 \(V\) 的 1 位都为 1(情况 2)。这两种情况分别对应我们枚举的
mask = V和某个构造的mask。在这两种情况下,路径上的每条边都满足(w & mask) == mask,因此在该mask下,询问点对一定连通。 - 反之,如果在某个
mask下询问点对连通,那么存在一条路径,其所有边都满足(w & mask) == mask,因此路径的按位与结果 \(X\) 也包含mask的所有 1 位。对于mask = V,\(X \supseteq V\),所以 \(X \ge V\);对于构造的mask,\(X\) 在高于 \(i\) 的 \(V\) 的 1 位上为 1,在第 \(i\) 位为 1 而 \(V\) 为 0,因此 \(X > V\)。两种情况都满足 \(X \ge V\)。
复杂度分析
- 掩码数量最多为 \(62\) 个(\(V\) 的位数)。
- 每个掩码需要遍历 \(m\) 条边和 \(q\) 个询问。
- 并查集操作近似 \(O(\alpha(n))\)。
- 总时间复杂度:\(O(62 \cdot (m + q) \cdot \alpha(n))\)。
- 空间复杂度:\(O(n + m + q)\)。
对于 \(n \le 10^5, m, q \le 5 \times 10^5\),该复杂度完全可以在 4 秒内通过。
实现细节与注意事项
-
\(V = 0\) 的情况
此时lowbit循环需要防止死循环。可以特判:如果 \(V=0\),直接加入mask = 0,它保留所有边,只需判断原图连通性。 -
并查集初始化
init()中需要将pa重置为自身,sz全部填充为 1。不要使用sz.clear(),否则会导致越界。 -
vis数组大小
用于标记询问是否已确定答案,大小应为 \(q + 1\),而不是 \(m + 1\)。 -
枚举范围
边权 \(< 2^{60}\),所以最高位到 60 即可。使用1LL << 62可以安全地构造高位掩码。 -
询问存储
使用vector<pair<int,int>>或vector<tuple<int,int,int>>存储询问,配合vis数组跳过已解决的询问。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll MOD = 1e9 + 7;
struct dsu {
int n;
vector<int> pa, sz;
dsu(int _n) : n(_n), pa(_n + 1), sz(_n + 1, 0) {init();}
void init() {
iota(pa.begin(), pa.end(), 0);
fill(sz.begin(), sz.end(), 1);
}
int findu(int x) {
if(pa[x] == x) return x;
return pa[x] = findu(pa[x]);
}
void unite(int x, int y) {
int rx = findu(x), ry = findu(y);
if(rx != ry) {
if(sz[rx] <= sz[ry]) pa[rx] = ry;
else pa[ry] = rx;
}
}
};
void solve() {
int n, m, q; ll v; cin >> n >> m >> q >> v;
vector<tuple<int, int, ll>> edge;
for(int i = 1; i <= m; i ++) {
int a, b; ll w; cin >> a >> b >> w;
edge.emplace_back(a, b, w);
}
vector<pair<int, int>> query(q + 1);
vector<int> vis(q + 1, 0);
for(int i = 1; i <= q; i ++) {
int u, v; cin >> u >> v;
query[i] = {u, v};
}
vector<ll> bitmask;
int lowbit = 0;
while(((v >> lowbit) & 1) == 0 && v != 0) lowbit ++;
for(int i = 61; i > lowbit; i --) {
if(((v >> i) & 1) == 1) continue;
ll mask = ((1LL << 62) - 1) - ((1LL << i) - 1);
mask = mask & v;
mask |= (1LL << i);
bitmask.push_back(mask);
}
bitmask.push_back(v);
int len = bitmask.size();
vector<int> ans(q + 1, 0);
dsu dsf(n);
for(int i = 0; i < len; i ++) {
dsf.init();
ll mask = bitmask[i];
for(int i = 0; i < m; i ++) {
auto [u, v, w] = edge[i];
if((w & mask) == mask) dsf.unite(u, v);
}
for(int i = 1; i <= q; i ++) {
if(vis[i]) continue;
auto [x, y] = query[i];
int rx = dsf.findu(x), ry = dsf.findu(y);
if(rx == ry) ans[i] = 1, vis[i] = 1;
}
}
for(int i = 1; i <= q; i ++) cout << (ans[i] ? "Yes\n" : "No\n");
}
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int _ = 1; //cin >> _;
while(_--) solve();
return 0;
}

浙公网安备 33010602011771号