Codeforces 1169E And Reachability 题解

博客探讨了Codeforces 1169E题目,涉及图论和位运算的算法。文章首先介绍了题目的背景和要求,即根据按位与运算判断在给定序列中是否存在路径。接着,提出了动态规划的解决方案,详细解释了如何处理dp状态转移和next数组的维护,以判断从xi能否到达yi。最后,提供了实现该思路的代码。

博客观赏效果更佳

题意简述

给定 n n n个数 a 1 , a 2... a n a1,a2...an a1,a2...an。如果 i < j i<j i<j a [ i ] & a [ j ] a[i]\&a[j] a[i]&a[j] & \& & 是按位与运算)非零,则 i → j i\rightarrow j ij连一条有向边。 q q q次询问,每次给定 x i xi xi y i yi yi,查询 x i xi xi是否能到 y i yi yi

每个输入的数都<=3e5,并且1<=xi<yi<=n。

思路框架

处理 d p [ i ] [ j ] dp[i][j] dp[i][j]表示 i i i往后第一个能到并且包含二进制第 j j j位的位置。

然后看是否存在 k k k使得 d p [ x ] [ k ] < y dp[x][k]<y dp[x][k]<y即珂。

具体思路

讲讲 n e x nex nex如何处理。显然 d p [ n + 1 ] [ x ] = n + 1 dp[n+1][x]=n+1 dp[n+1][x]=n+1,对于所有 x x x。维护 d p dp dp同时维护一个 n e x [ ] nex[] nex[] n e x [ j ] nex[j] nex[j]表示当前位置往后第一个包含二进制第 j j j位的数在哪个位置。

显然, i → n e x [ j ] i\rightarrow nex[j] inex[j]是联通的( & \& &一下,至少还有第 j j j位)。
然后对于一个 i i i,枚举 j j j如果 i i i包含第 j j j位,则对于所有 k k k,用 d p [ n e x [ j ] ] [ k ] dp[nex[j]][k] dp[nex[j]][k]更新 d p [ i ] [ k ] dp[i][k] dp[i][k]的最小值。

然后我们不是枚举 i i i中包含第 j j j位么,更新完 d p dp dp之后,更新 n e x nex nex n e x [ j ] = i nex[j]=i nex[j]=i。然后记得 d p [ i ] [ j ] = i dp[i][j]=i dp[i][j]=i。(自己也能到自己)

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define N 1666666
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define p_b push_back
    #define sz(a) ((int)a.size())
    #define iter(a,p) (a.begin()+p)
    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    void Rd(int cnt,...)
    {
        va_list args;
        va_start(args,cnt);
        F(i,1,cnt) 
        {
            int* x=va_arg(args,int*);R1(*x);
        }
        va_end(args);
    }

    int n,q,a[N];
    void Input()
    {
        Rd(2,&n,&q);F(i,1,n) R1(a[i]);
    }

    int dp[N][22];
    int nex[22];
    void Soviet()
    {
        MEM(dp,0x3f);
        F(i,0,20) nex[i]=n+1,dp[n+1][i]=n+1;
        D(i,n,1) F(j,0,20) if (a[i]&(1<<j))
        {
            F(k,0,20) dp[i][k]=min(dp[i][k],dp[nex[j]][k]); 
            dp[i][j]=i;
            nex[j]=i;
        }

        F(i,1,q)
        {
            int x,y;Rd(2,&x,&y);
            bool flag=0;
            F(j,0,20) 
            {
                if ((a[y]&(1<<j)) and dp[x][j]<=y) {flag=1;break;} //找到一个就有解
            }
            puts(flag?"Shi":"Fou");
        }
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值