【Codeforces 1168C】And Reachability 思维dp

题目链接: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
**/

猜你喜欢

转载自blog.csdn.net/qq_43857314/article/details/106694539