题意:给你一个长度为n的序列,有q次询问,每次询问给出两个位置x和y(x < y),问是否可从x到达y?可达的定义是:如果存在一个序列(假设长度为k),其中p1 = x, pk = y,并且这个序列中a[pi] & a[p(i + 1)] != 0。
思路:设dp[i][j]是从i位置及其之后的位置中,二进制位的第j位为1,并且可达的最靠前的位置,设last[j]是已经扫描过的数中第j位为1的最靠前的位置。我们考虑怎么求dp[i][j]。首先,如果a[i]的二进制位的第j位为1,那么dp[i][j] = i,而且i位置到last[j]一定是可达的,那么我们可以用dp[last[j]][k]来更新dp[i][k]。询问的时候,如果a[y]的第j位为1,并且dp[x][j] <= y, 那么就可达,否则不可达。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 300010; int dp[maxn][20], last[20]; int a[maxn]; int main() { int n, T; scanf("%d%d", &n, &T); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); } for (int i = 0; i < 20; i++) { last[i] = n + 1; dp[n + 1][i] = n + 1; } for (int i = n; i >= 1; i--) { for (int j = 0; j < 20; j++) { dp[i][j] = n + 1; } for (int j = 0; j < 20; j++) { if((a[i] >> j) & 1) { for (int k = 0; k < 20; k++) { dp[i][k] = min(dp[i][k], dp[last[j]][k]); } last[j] = i; dp[i][j] = i; } } } while(T--) { int x, y; scanf("%d%d", &x, &y); bool flag = 0; for (int i = 0; i < 20; i++) { if(((a[y] >> i) & 1) && dp[x][i] <= y) { flag = 1; break; } } if(flag) printf("Shi "); else printf("Fou "); } }