Toad Pimple 有一个整数数组 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,我们称 x x x到 y y y可达当且仅当:
现在给定 Q Q Q组下标 ( X i , Y i ) (X_i,Y_i) (Xi,Yi),请检查它们是否可达。
考虑从 y y y 跳到 x x x,如果我们可以找到一个 a i a_i ai 满足第 j j j 位是 1 1 1 且 a l a_l al 在这一位也是 1 1 1,并且 r r r 可以跳到这个位置 i i i,就是可行的。
显然,我们要尽可能让这个位置贴近 r r r,因为这样更有“优势”,可以让更多的 l l l 可行。
设 f i , j f_{i,j} fi,j 表示一个最大的下标 x x x 满足小于等于 i i i, a x a_x ax 第 j j j 位是 1 1 1。
有朴素转移: f i , j = max k = 1 i − 1 f k , j f_{i,j}=\max\limits_{k=1}^{i-1}f_{k,j} fi,j=k=1maxi−1fk,j,转移条件 a i & a k > 0 a_i\&a_k>0 ai&ak>0;特殊地,当 a i a_i ai 第 j j j 为是 1 1 1 时, f i , j = i f_{i,j}=i fi,j=i。
这是 O ( n 2 ) O(n^2) O(n2) 的,思考如何优化。
发现转移条件 a i & a k > 0 a_i\&a_k>0 ai&ak>0,可以转换为枚举第 x x x 位,如果 a i a_i ai 和 a k a_k ak 的第 x x x 为都为 1 1 1,这样就可以转移。
又有性质 f i , j f_{i,j} fi,j 随 i i i 单调递增,那么肯定是选最大的 k k k 一定最优。
所以用 p r e j pre_j prej 记录当前上一个下标 x x x 满足 a x a_x ax 的第 j j j 位为 1 1 1。转移就变成
f i , j = max k = 0 m f p r e k , j f_{i,j}=\max\limits_{k=0}^{m}f_{pre_{k},j} fi,j=k=0maxmfprek,j
对每个 i i i 转移完后记得更新 p r e j pre_j prej。
时间复杂度 O ( n log 2 n + q log n ) O(n\log^2n+q\log n) O(nlog2n+qlogn)
#include
using namespace std;
const int N=3e5+1,NN=19;
int n,q,a[N],f[N][NN+1],pre[NN];
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=0;j<NN;j++){
for(int k=0;k<NN;k++){
if(a[i]>>k&1){
f[i][j]=max(f[i][j],f[pre[k]][j]);
}
}
}
for(int j=0;j<NN;j++){
if(a[i]>>j&1){
pre[j]=i;
f[i][j]=max(f[i][j],i);
}
}
}
for(int i=1,x,y;i<=q;i++){
scanf("%d%d",&x,&y);
for(int j=0;j<NN;j++){
if(a[x]>>j&1){
if(f[y][j]>=x){
puts("Shi");
goto a;
}
}
}
puts("Fou");
a:;
}
}