Codeforces 1169E DP

题意:给你一个长度为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\n");
		else printf("Fou\n");
	}
}

  

posted @ 2019-05-27 20:29  维和战艇机  阅读(577)  评论(0编辑  收藏  举报