Codeforces 1168C And Reachability

Codeforces 1168C And Reachability

题目链接

https://codeforces.com/contest/1168/problem/C

题目大意

给你一个长度为 $n$ 的数列 $a$ ,$q$ 次询问,问是否存在数列 $p$ 使得有 $x=p_1<p_2<\cdots<p_k=y$,且 $a_{p_i} \& a_{p_{i+1}}>0(1 \leq i < k)$ 成立。

题目分析

看到有 $q$ 次询问,可以想到需要预处理一些东西之后迅速查询。观察给的式子,可以发现,如果 $a_x$ 能走到 $a_y=2^i$ ,那么所有包含 $2^i$ 且位置大于等于 $y$ 的都可以走到。因此显然可以将所有的位拆解,对于每一个位置求走到 $2^i$ 的最小需要的右端点位置。求这个显然只需要记录一下每一位出现的最后一次的位置,再 $DP$ 一下就可以。

查询的时候枚举一下是通过哪个 $2^i$ 走过去的,判断一下右端点位置是否满足要求即可。

代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 3e5 + 5;
const int inf = 0x3f3f3f3f;

int n, m;

int a[maxn];

int f[maxn][21];

int last[21];

int main(){
	int i, j, k;
	int x, y;
	memset(f, 0x3f, sizeof(f));
	memset(last, 0x3f, sizeof(last));
	
	scanf("%d%d", &n, &m);
	
	for(i=1;i<=n;i++){
		scanf("%d", &a[i]);
	}
	
	for(i=n;i>0;i--){
		for(j=0;j<=20;j++){
			if((a[i] >> j) & 1){
				f[i][j] = i;
				if(last[j] != inf){
					for(k=0;k<=20;k++){
						f[i][k] = min(f[i][k], f[last[j]][k]);
					}
				}
				last[j] = i;
			}
		}
	}
	
	while(m--){
		scanf("%d%d", &x, &y);
		bool flag = false;
		for(j=0;j<=20;j++){
			if((a[y] >> j) & 1){
				if(y >= f[x][j]){
					flag = true;
					break;
				}
			}
		}
		if(flag){
			printf("Shi\n");
		}else{
			printf("Fou\n");
		}
	}
	
	return 0;
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据