很有帮助的拆位题
判断u、v之间有无“所有边权相与”>=V的路径
1 & 1 = 1, 1 & 0 = 0, 0 & 1 = 0, 0 & 0 = 0
一条路径上所有边权的与值A大于等于V有两种情况:
①A > V V的第i位为0时,A的这一位为1,前面与V相同,后面都为0(后面的每一位值其实都可以任意,但如果1多的话后面删边会删的很多,漏掉不少情况,1的数量保持最少可以囊括更多情况,做到不重不漏)
②A = V
然后保留某些边,余下的这些边s, 满足s & A = A,也就是A中为1的s也为1,这样的边才能保留下来
我们用并查集判断联通,复杂度为O(m * 62)
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define endl '\n'
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
typedef long double ld;
const int N = 100010, M = 500010;
struct Node
{
int a, b;
ll w;
}edges[M];
int p[N];
int n, m, Q;
ll V;
bool ans[M];
PII que[M];
int find(int x)
{
if(p[x] == x)return x;
return p[x] = find(p[x]);
}
int main()
{
IOS
cin >> n >> m >> Q >> V;
for(int i = 1; i <= m; i ++)
{
int a, b;
ll w;
cin >> a >> b >> w;
edges[i] = {a, b, w};
}
for(int i = 1; i <= Q; i ++)
{
int a, b;
cin >> a >> b;
que[i] = {a, b};
}
for(int i = 60; i >= 0; i --)
{
if(V >> i & 1)continue;
for(int i = 1; i <= n; i ++)p[i] = i;
ll res = ((V >> i) + 1) << i;
for(int j = 1; j <= m; j ++)
{
int a = edges[j].a, b = edges[j].b;
ll w = edges[j].w;
if((w & res) != res)continue;
int pa = find(a), pb = find(b);
if(pa != pb)
{
p[pa] = pb;
}
}
for(int j = 1; j <= Q; j ++)
{
int pa = find(que[j].first), pb = find(que[j].second);
if(pa == pb)ans[j] = true;
}
}
for(int i = 1; i <= n; i ++)p[i] = i;
ll res = V;
for(int j = 1; j <= m; j ++)
{
int a = edges[j].a, b = edges[j].b;
ll w = edges[j].w;
if((w & res) != res)continue;
int pa = find(a), pb = find(b);
if(pa != pb)
{
p[pa] = pb;
}
}
for(int j = 1; j <= Q; j ++)
{
int pa = find(que[j].first), pb = find(que[j].second);
if(pa == pb)ans[j] = true;
}
for(int i = 1; i <= Q; i ++)
{
if(ans[i])cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}