题目链接
题意
给你长为 n n n 的数组, q q q 次查询,每次查询两个下标, x , y x,y x,y,问是否存在 x − > y x->y x−>y 的路径,路径满足 x = p 1 < p 2 < … < p k = y x=p1<p2<…<pk=y x=p1<p2<…<pk=y 而且 a p i a p i + 1 > 0 a_{pi}a_{pi+1}>0 apiapi+1>0
思路
设dp数组 dp[第i个点][到达二进制某一位] = 最小的下标
转移倒着转移,用
l
s
t
lst
lst 数组记录每一位最近一次出现位置。
每次转移考虑当前点第
j
j
j 位是否存在,存在则从当前点
l
s
t
[
j
位
]
lst[j位]
lst[j位] 处转移。然后更新
l
s
t
[
j
]
=
i
,
d
p
[
i
]
[
j
]
=
i
lst[j] = i,dp[i][j] = i
lst[j]=i,dp[i][j]=i
每次查询,判断
a
[
y
]
a[y]
a[y] 的某位
j
j
j 是否存在,存在继续判断
d
p
[
x
]
[
i
]
是
否
小
于
y
dp[x][i]是否小于y
dp[x][i]是否小于y 是则存在路径。
代码
#include <bits/stdc++.h>
using namespace std;
int a[300005], dp[300005][20], lst[25];
int main() {
int n, q;
scanf("%d%d",&n,&q);
for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
for(int i = 0; i < 20; ++i) lst[i] = n+1;
for(int i = n; i; --i) {
for(int j = 0; j < 20; ++j) dp[i][j] = n+1;
for(int j = 0; j < 20; ++j) if((1<<j)&a[i]) {
if(lst[j]) {
for(int k = 0; k < 20; ++k) dp[i][k] = min(dp[i][k], dp[lst[j]][k]);
}
lst[j] = i;
dp[i][j] = i;
}
}
while(q--) {
int x, y, f = 0;
scanf("%d%d",&x,&y);
for(int i = 0; i < 20; ++i) if(((1<<i)&a[y]) && dp[x][i] <= y) f = 1;
printf("%s\n",f?"Shi":"Fou");
}
return 0;
}