CF1168C And Reachability 【构造,dp】

题目链接:洛谷

题目描述:给出$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)$

 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 }
CF1168C

 

posted @ 2019-07-02 07:25  mizu164  阅读(324)  评论(0编辑  收藏  举报