题目传送门

位运算

设题目中序列 \(w_1,w_1\& w_2,w_1\& w_2\& w_3,\dots,w_1\& w_2\& \dots \& w_n\) 为序列 \(A\)。

因为是数字一个一个 \(\&\) 到之前的结果上,所以可以知道 \(A\) 序列单调不增。

从给出的样例中发现,似乎没有答案超过 \(2\) 的情况

证明:

假设答案 \(>2\),则说明 \(A\) 序列至少中出现过了 \(0,1,2\),因为 \(A\) 序列单调不增,所以假设当前的 \(A_i\) 为 \(2\),必须后面的数出现 \(1\) 和 \(0\) 才可以。但 \(2\) 的二进制末位为 \(0\),无论怎么 \(\&\) ,后面的数也不可能出现 \(1\),假设不成立,所以答案不可能 \(>2\)。

结论:

答案为 \(0\),\(1\) 或 \(2\)。

判断答案

  • 首先考虑答案为 \(0\) 的情况,即 \(A\) 序列中没有出现过 \(0\):

\(A\) 序列中没有出现过 \(0\),即说明对于所有 \(w\),在二进制下至少有一位都是 \(1\),不然 \(\&\) 后就会变 \(0\)。

那么如何判断是否有一条路径上所有边权在二进制下至少有一位都是 \(1\)?

考虑用并查集维护。因为 \(w<=2^{30}\),所以可以开 \(30\) 个并查集,分别维护每一位上是 \(1\) 的边权所连接的点的集合,然后要判断求的两点是否在其中一张图中连通即可。

  • 再看答案为 \(1\) 的情况,即 \(A\) 序列中出现过 \(0\) 但没有出现过 \(1\):

\(A\) 序列中没有出现过 \(1\),即说明对于 \(A\) 序列前某一部分 \((i<k)\),\(a_i>1\),而 \(a_k\) 及之后都是 \(0\)。

先考虑如何保证 \(A\) 序列前某一部分 \((i<k)\),\(a_i>1\)。这个和答案为 \(0\) 的情况很像,只要所有 \(w_i(i<k)\) 中至少有一位都是 \(1\) 即可。但特别地,这一位不能是末位,否则 \(a_{k-1}\) 就变 \(1\) 了。

然后就只要判断之后是否有一条边可以使 \(\&\) 之后结果为 \(0\) 即可。

如何维护?

先上结论:只要保证末位有一个 \(0\) 即可。先把之前判断答案是否为 \(0\) 的并查集借过来,然后事先找好哪些边权末位为 \(0\),将与这些边相邻的点和一个虚点 \(0\) 连起来。如果后询问中出发点 \(u\) 可以和虚点 \(0\) 连通,那么答案就为 \(1\) 了。

证明:如果后询问中出发点 \(u\) 可以和虚点 \(0\) 连通,就意味着 \(u\) 在某一位上 (不为末位)可经过连续的几个 \(1\),保证了 \(a_i>1\)。然后可以走到一位末位为 \(0\) 的,\(a_i\) 的末位就会变成 \(0\)。之后,因为不存在某一位全部为 \(1\) 的(已经被判掉了),所以可以保证 \(a_i\) 的其他位最终也会变成 \(0\)。

  • 最后,都不是这两种情况的答案就为 \(2\)。

那么,如何方便地实现并查集?

便利の并查集

可以写到一个结构体里。(涨芝士了

比如:

struct DSU{
int fa[100005];
DSU(){for(int i=0;i<=100000;i++)fa[i]=i;} //初始化
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} //find
void merge(int x,int y){fa[find(x)]=find(y);} //合并
bool query(int x,int y){return find(x)==find(y);} //判断
}x[35];

合并:

x[i].merge(u,v);

判断联通:

if(x[i].query(u,v))

要开多个并查集时很方便,比写二维数组要清楚。

完整代码

#include<bits/stdc++.h>
using namespace std;
int n,m,q,u,v,w;
bool mark[100005];
struct DSU{ //还是喜欢不压行
int fa[100005];
DSU(){
for(int i=0;i<=100000;i++) fa[i]=i;
}
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
void merge(int u,int v){
fa[find(u)]=find(v);
return ;
}
bool query(int u,int v){
return find(u)==find(v);
}
}x[35],y[35];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
for(int j=0;j<30;j++){
if((w>>j)&1) x[j].merge(u,v); //j位是1的连起来
}
if(!(w&1)) mark[u]=mark[v]=1; //末位不是1做标记
}
for(int i=1;i<=30;i++){ //从1开始
y[i]=x[i];
for(int j=1;j<=n;j++){
if(mark[j]) y[i].merge(j,0); //有标记和0连
}
}
scanf("%d",&q);
for(int i=1;i<=q;i++){
scanf("%d%d",&u,&v);
bool fl=0;
for(int j=0;j<30;j++){ //从0开始
if(x[j].query(u,v)){
printf("0\n"),fl=1;
break;
}
}
if(fl) continue;
for(int j=1;j<30;j++){ //从1开始
if(y[j].query(u,0)){
printf("1\n"),fl=1;
break;
}
}
if(fl) continue;
printf("2\n");
}
return 0;
}

特别鸣谢:巨佬 Mine_King代码 帮助我理解了解题方法

查重率 100% qwq

最新文章

  1. eclipse提示:This tag and its children can be replaced by one &lt;TextView/&gt; and a compound drawable
  2. 【leetcode】Minimum Path Sum(easy)
  3. 全半角空格导致的Sql Server Analysis Services处理错误(转载)
  4. ORACLE TM锁
  5. select count(*)和select count(1)的区别 (转)
  6. Unity 编辑器扩展自定义窗体
  7. Javascript知识四(DOM)
  8. navigator获取参数
  9. 【CSS学习笔记】超链接标签
  10. SSH:dataSource配置问题
  11. caffe+opencv3.3dnn模块 完成手写数字图片识别
  12. awk 手册
  13. Appium 连手机失败Error: Android bootstrap socket crashed: Error: getaddrinfo ENOTFOUND localhost undefined:4724
  14. ionic 编写自定义控件
  15. 35. oracle中instr在平台上的转换用法
  16. Utils使用
  17. Codeforces Round #295 (Div. 2)C - DNA Alignment 数学题
  18. ballerina 学习十三 函数&amp;&amp;documentation
  19. 把mysql的数据导出成txt
  20. 【转】Android系统概览

热门文章

  1. 【JDBC】学习路径10-c3p0数据源的使用(JDBC完结)
  2. 【漏洞分析】KaoyaSwap 安全事件分析
  3. 【AGC】使用云调试优惠扣费、华为设备上触发崩溃、无法下载华为应用市场问题小结
  4. Stream流式计算
  5. Job for redis-server.service failed because the control process exited with error code(Centos 7 设置Redis开机自启报错)
  6. 使用logstash读取MySQL数据传输到es,并且@timestamp字段采用MySQL中的字段时间--建议采用这个
  7. 多字段特性及配置自定义Analyzer
  8. 监控MySQL运行状态:MySQLD Exporter
  9. 【前端必会】不知道webpack插件? webpack插件源码分析BannerPlugin
  10. 关于Linux中使用bc命令计算圆周率