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

链接CodeForces -1208F Bits And Pieces

题意:

给出一个长度为 n    ( 3 n 1 0 6 ) n\;(3\le n\le 10^6) 的序列 a 1 , a 2 ,   , a n    ( 0 a i 2 1 0 6 ) a_1,a_2,\cdots,a_n\;(0\le a_i\le 2\cdot10^6) ,求 a i ( a j & a k )    ( i < j < k ) a_i|(a_j\And a_k)\;(其中 i\lt j\lt k) 的最大值。



分析:

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

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


①dp过程

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

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

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

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

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


②求解答案

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



以下代码:

#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;
}

发布了214 篇原创文章 · 获赞 40 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Ratina/article/details/100100711