CF1168C And Reachability

CF1168C And Reachability

洛谷CF1168C And Reachability

题目大意

有一个数组 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,,an,我们称 x x x y y y可达当且仅当:

  • x < y xx<y
  • 存在一个数组 p p p,其中 x = p 1 < p 2 < ⋯ < p k = y x=p_1x=p1<p2<<pk=y,且对于任意 1 ≤ i < k 1\leq i1i<k,满足 a p i & a p i + 1 > 0 a_{p_i}\& a_{p_i+1}>0 api&api+1>0

q q q组询问,第 i i i组询问给出 x i x_i xi y i y_i yi,求它们是否可达。如果可达,输出 S h i Shi Shi;否则,输出 F o u Fou Fou

1 ≤ n ≤ 3 × 1 0 5 , 1 ≤ q ≤ 3 × 1 0 5 , x i < y i 1\leq n\leq 3\times 10^5,1\leq q\leq 3\times 10^5,x_i1n3×105,1q3×105,xi<yi


题解

首先,如果 x x x y y y可达, y y y z z z可达,则 x x x z z z可达。

对于一个点 i i i,如果 a i & 2 k > 0 a_i\& 2^k>0 ai&2k>0,则对于所有 j > i j>i j>i a j & 2 k > 0 a_j\& 2^k>0 aj&2k>0 i i i j j j都是可达的。

w i , k w_{i,k} wi,k表示 i i i可达的最小的 j j j(这里,我们定义当 a i > 0 a_i>0 ai>0时, i i i i i i是可达的),那么,对于所有 j > i j>i j>i,只要满足存在一个 k k k使得 a j & 2 k > 0 a_j\& 2^k>0 aj&2k>0 w i , k ≤ j w_{i,k}\leq j wi,kj,则 i i i j j j可达。

那如何求 w i , k w_{i,k} wi,k呢?对于每个 w i , k w_{i,k} wi,k,我们只需求出满足 j > i j>i j>i a j & 2 k > 0 a_j\&2^k>0 aj&2k>0的最小的 j j j,对于每个 0 ≤ t ≤ log ⁡ n 0\leq t\leq \log n 0tlogn,都令 w i , k = min ⁡ ( w i , k , w j , k ) w_{i,k}=\min(w_{i,k},w_{j,k}) wi,k=min(wi,k,wj,k)

考虑 w i , k w_{i,k} wi,k的初始值。如果 a i & 2 k > 0 a_i\&2^k>0 ai&2k>0 w i , k = i w_{i,k}=i wi,k=i;否则, w i , k = n + 1 w_{i,k}=n+1 wi,k=n+1

求出 w i , k w_{i,k} wi,k之后,对于每组 x , y x,y x,y,如果存在一个 k k k使得 a y & 2 k > 0 a_y\&2^k>0 ay&2k>0 w x , k ≤ y w_{x,k}\leq y wx,ky,输出 S h i Shi Shi;否则,输出 F o u Fou Fou

时间复杂度为 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)

code

#include
using namespace std;
int n,q,x,y,fl,a[300005],lst[20],w[300005][20];
int main()
{
//	freopen("and.in","r",stdin);
//	freopen("and.out","w",stdout);
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){	
		scanf("%d",&a[i]);
		for(int j=0;j<=19;j++){
			if((a[i]>>j)&1) w[i][j]=i;
			else w[i][j]=n+1;
		}
	}
	for(int i=n;i>=1;i--){
		for(int j=0;j<=19;j++){
			if(!((a[i]>>j)&1)) continue;
			if(lst[j])
			for(int k=0;k<=19;k++){
				w[i][k]=min(w[i][k],w[lst[j]][k]);
			}
			lst[j]=i;
		}
	}
	while(q--){
		scanf("%d%d",&x,&y);
		fl=0;
		for(int i=0;i<=19;i++){
			if(((a[y]>>i)&1)&&w[x][i]<=y) fl=1;
		}
		if(fl) printf("Shi\n");
		else printf("Fou\n");
	}
	return 0;
}

你可能感兴趣的:(题解,题解,c++)