题目链接:洛谷
题目描述:给出$n$个数$a_i$,若$i<j$且$a_i & a_j>0$,则$i$到$j$连一条有向边,$q$次询问,询问从$l$开始是否能到达$r$。
数据范围:$n,q\leq 3*10^5$
一道及其美妙的思维题。(以下均用二进制)
维护两个数组,$g_{i,j}$表示下标小于$i$的数中下标最大且第$j$位为1的下标。$f_{i,j}$表示下标小于$i$的数中下标最大,第$j$位为1且可以到达$i$的下标。
$g_{i,j}$的预处理不用多说了,$f_{i,j}$预处理时可以枚举一位$k$,表示$a_i$通过第$k$位可以从$g_{i,k}$到达,用$g_{i,k}$的f值来直接推出$i$的f值。
询问的时候,枚举一位$i$,表示$r$可以从$f_{r,i}$到达,而当$a_l$的第$i$位为1且$f_{r,i}\geq l$时$l$可以到达$f_{r,i}$,所以$l$可以到达$r$。如果对于每一位都不行则输出fou。
时间复杂度$O(n\log^2n+q\log n)$
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 #define Rint register int 3 using namespace std; 4 const int N = 300001; 5 int n, m, f[N][19], g[N][19]; 6 bool a[N][19]; 7 int main(){ 8 scanf("%d%d", &n, &m); 9 for(Rint i = 1;i <= n;i ++){ 10 int x; 11 scanf("%d", &x); 12 for(Rint j = 0;j < 19;j ++) 13 if(x & (1 << j)) a[i][j] = 1; 14 } 15 for(Rint i = 1;i <= n;i ++) 16 for(Rint j = 0;j < 19;j ++) 17 if(a[i - 1][j]) g[i][j] = i - 1; 18 else g[i][j] = g[i - 1][j]; 19 for(Rint i = 1;i <= n;i ++) 20 for(Rint j = 0;j < 19;j ++) 21 for(Rint k = 0;k < 19;k ++) 22 if(a[i][k]){ 23 int x = g[i][k]; 24 f[i][j] = max(f[i][j], f[x][j]); 25 if(a[x][j]) f[i][j] = max(f[i][j], x); 26 } 27 while(m --){ 28 int l, r; 29 bool ans = false; 30 scanf("%d%d", &l, &r); 31 for(Rint i = 0;i < 19 && !ans;i ++) 32 if(a[l][i] && f[r][i] >= l) ans = true; 33 puts(ans ? "Shi" : "Fou"); 34 } 35 }