题目链接:https://codeforces.ml/contest/1168/problem/C
题目大意:
给出一段序列a_i,定义x能够到达y,则a_x&a_y > 0,y>x
m次询问,给出x,y ,问x是否可以间接或者直接到达y
题目思路:
题解有点秀
首先肯定的是到达y点,那么到达y点之前的一个点p必然存在一个k ,使得 a_p>>k&1 = a_y>>k&1 ,即y和之前某个点在某一位上都为1,这时通过该点即可到达y
但是中间过渡可能是不连续的:1 3 2 1->3->2这也是可以的
首先考虑dp解决该问题,dp[i][k]表示从i这个位置出发,能到达的第一个第k位为1的数的下标
那么为了防止1 3 2 这种情况:
如果当前数当前位j为1,那么dp[i][j] = i
否则,他仍有可能到达第j位,不过此时应该贪心的去考虑:
记录一个后缀last[j]表示,上一个第j位是1的位置,因为目标是尽可能的让dp[i][j]更小。(记录之后的第j位是1的没有什么意义)
所以当前这个数第k位是1时,还可以判断一下dp[last[k]][j] 的大小(此时间接联通)
处理完dp数组,就可以O1判断询问了。
当且仅当num[y]第k位是1且f[x][k] <= y 那么就可以转移过去了
Note:注意边界的初始值
Code:
/*** keep hungry and calm CoolGuang!***/
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=1e18;
const int maxn=6e5+6;
const int mod=100000000;
const double eps=1e-15;
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
ll num[maxn];
int f[maxn][25];
int last[25];
int main(){
read(n);read(m);
for(int i=1;i<=n;i++) read(num[i]);
for(int i=0;i<=n;i++){
for(int k=0;k<=20;k++){
f[i][k] = 1e9+7;
}
}
for(int i=n;i>=1;i--){
for(int j=0;j<=20;j++){
if(num[i]>>j&1) f[i][j] = i;
else{
for(int k=0;k<=20;k++){
if(num[i]>>k&1){
f[i][j] = min(f[i][j],f[last[k]][j]);
}
}
}
}
for(int j=0;j<=20;j++)
if(num[i]>>j&1) last[j] =i;
}
for(int i=1;i<=m;i++){
int x,y;scanf("%d%d",&x,&y);
int ft = 0;
for(int k=0;k<=20;k++){
if(num[y]>>k&1&&f[x][k]<=y)
ft = 1;
}
if(ft) printf("Shi\n");
else printf("Fou\n");
}
return 0;
}
/**
6 12 36
**/