CodeForces -1208F Bits And Pieces(位运算,贪心,SoS DP)

博客详细分析了CodeForces上的一道题目1208F(Bits And Pieces),讨论如何通过位运算和贪心策略解决寻找序列中满足条件的最大值的问题。作者解释了固定一个元素后,如何通过二进制位的分析确定最佳匹配,并引入SoS DP的概念来优化解决方案。最后,提供了问题的解题思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链接CodeForces -1208F Bits And Pieces

题意:

给出一个长度为 n &ThickSpace; ( 3 ≤ n ≤ 1 0 6 ) n\;(3\le n\le 10^6) n(3n106) 的序列 a 1 , a 2 , ⋯ &ThinSpace; , a n &ThickSpace; ( 0 ≤ a i ≤ 2 ⋅ 1 0 6 ) a_1,a_2,\cdots,a_n\;(0\le a_i\le 2\cdot10^6) a1,a2,,an(0ai2106),求 a i ∣ ( a j &amp; a k ) &ThickSpace; ( 其 中 i &lt; j &lt; k ) a_i|(a_j\And a_k)\;(其中 i\lt j\lt k) ai(aj&ak)(i<j<k) 的最大值。



分析:

假设固定一个 a i a_i ai,对于为 1 1 1的二进制位,或运算一定得 1 1 1,不用考虑;但对于为 0 0 0的二进制位,只有和 1 1 1或运算才能使得结果值更大,也就是说要 a j a_j aj a k a_k ak 的对应二进制位为 1 1 1(因为 a i a_i ai a j a_j aj之间是与运算)。

例如当 a i = 0100 &ThinSpace; 1101 a_i=0100\,1101 ai=01001101(假设 a ≤ 2 8 − 1 a\le2^8-1 a281),则要令值最大应当尽可能找到 a j a_j aj a k a_k ak,其子集均包括 1011 &ThinSpace; 0010 1011\,0010 10110010


①dp过程

这里就要用到SoS DP的思想:https://codeforces.com/blog/entry/45223

d p [ x ] dp[x] dp[x]序列中 子集含有 x x x a a a的位置集合,由于 i &lt; j &lt; k i\lt j\lt k i<j<k,所以只要贪心地 保留位置最靠右(最大)的两个位置 即可。

所以可以先将所有 a a a放入 d p dp dp数组当中,对于 x x x,其 子集有 2 k 2^k 2k(其中 k k k x x x二进制位为 1 1 1的个数),若对于每一个 d p [ a ] dp[a] dp[a],都找到所有子集并更新,时间复杂度会达到 O ( N ∗ a ) O(N*a) O(Na)

由于很多 a a a都有相同的子集,所以多了很多重复更新。我们可以考虑,一个数,其子集的子集,仍然是其子集,所以可以 每次仅更新将 其中一位二进制位从 1 1 1变为 0 0 0 的子集(之后这些子集再去更新下一层子集)。例如 a = 1101 a=1101 a=1101,则仅更新 0101 , 1001 , 1100 0101,1001,1100 0101,1001,1100

由于子集肯定小于原数,那么可以从最大的 a m a x a_{max} amax开始,从后往前更新 0 0 0 ~ a m a x a_{max} amax的所有数,时间复杂度为 O ( a ∗ k ) O(a*k) O(ak)


②求解答案

枚举 a i a_i ai,根据 a i a_i ai的二进制位 贪心地构造一个值 w w w,保证 d p [ w ] dp[w] dp[w]的两个最大位置均 &gt; i \gt i >i,最大的 a i ∣ w a_i|w aiw即为答案。



以下代码:

#include<bits/stdc++.h>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e6+50;
const int maxa=2e6+50;
int n,a[maxn];
PII dp[maxa];
void updata(int x,int pos)
{
    if(dp[x].first<pos)
    {
        dp[x].second=dp[x].first;
        dp[x].first=pos;
    }
    else if(dp[x].second<pos&&pos!=dp[x].first)  //注意first和second的位置不能相同
        dp[x].second=pos;
}
void get_dp()
{
    for(int x=maxa-1;x>=0;x--)   //从大到小更新
    {
        for(int i=20;i>=0;i--)
        {
            if(((x>>i)&1)==1)    //枚举x为1的二进制位
            {
                updata(x^(1<<i),dp[x].first);     //更新 把x的一个为1的二进制位变为0的子集
                updata(x^(1<<i),dp[x].second);
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        updata(a[i],i);
    }
    get_dp();
    int ans=0;
    for(int i=1;i<=n-2;i++)
    {
        int w=0;
        for(int j=20;j>=0;j--)
        {       //找到a[i]为0的二进制位
            if(((a[i]>>j)&1)==0&&dp[w|(1<<j)].first>i&&dp[w|(1<<j)].second>i)
                w=w|(1<<j);
        }
        ans=max(ans,a[i]|w);
    }
    printf("%d",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值