[SDCPC2023] Not Another Path Query Problem
还记得如何比较两个数的大小吗?在两个数数位长度都相同的情况下,从高位开始依次比较。题目中数据的值域为 ,我们比较最多约 次即可得知答案。
我们依次考虑 二进制前缀的若干位。
开 个并查集。对于长度为 后缀,我们将其最后一位强制赋上 ,记这个值为 。
然后,倘若当前边长度为 的后缀中, 所有为 的位 都为 ,就把这条边加进并查集 。这样,可以保证并查集 的所有边之与运算之和,其长度为 的前缀大于等于 。
证明:二进制意义下, 的一个必要条件是存在某一位 , 的第 位为 , 的第 位为 。而在我们的操作中,保证了 为 的所有位,边权的当前位也必定为 。该必要条件无法被满足。
查询时,从高位到低位扫一遍 个并查集。若能遍历到这一位,说明我们已经考虑过前面的位了,只关心这一位的内容即可。每一位根据 的二进制位分类讨论:
- 的当前位为 :我们加入并查集的时候最后一位被修改为了 。倘若并查集告诉我们这两个点联通,则这两点之间存在一条略大于 的路径,直接打飞,否则继续枚举;
- 的当前位为 :此时并查集存入的正是 的前缀。若两点不联通,就肯定不存在大于等于 的路径了,直接打飞,否则继续枚举。
不过,这套方法只能考虑到是否存在大于 的边。特殊处理一下,再单独拉个并查集把等于的扔进去就对了。
#include <cstddef>
#include <iostream>
#include <vector>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
using Graph = vector<vector<size_t>>;
struct DSU {
vector<ptrdiff_t> fa;
DSU(size_t n): fa(n, -1) {}
size_t find(size_t x) { return fa[x] < 0 ? x : fa[x] = find(fa[x]); }
size_t setSize(size_t x) { return -fa[find(x)]; }
bool check(size_t x, size_t y) { return find(x) == find(y); }
void merge(size_t x, size_t y) {
if (check(x, y)) return;
if (setSize(x) > setSize(y)) swap(x, y);
fa[find(y)] -= setSize(x);
fa[find(x)] = find(y);
}
};
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t n, m, q;
uli k;
fin >> n >> m >> q >> k;
vector<DSU> ds(61, DSU{n});
while (m--) {
size_t u, v;
uli w;
fin >> u >> v >> w;
--u, --v;
for (size_t i = 0; i < 60; ++i)
if (((w >> i) & (k >> i | 1)) == (k >> i | 1)) ds[i].merge(u, v);
if ((w & k) == k) ds[60].merge(u, v);
}
while (q--) {
size_t x, y;
fin >> x >> y;
--x, --y;
if (ds[60].check(x, y)) goto yes;
for (size_t i = 59; i < 60; --i) {
bool tst = ds[i].check(x, y);
if (((k >> i) & 1) && !tst) goto no;
if (!((k >> i) & 1) && tst) goto yes;
}
no:
fout << "No\n";
continue;
yes:
fout << "Yes\n";
}
return 0;
}