codeforces 165E Compatible Numbers

昨天看到一道好题,现与大家分享一下。

http://codeforces.com/contest/165/problem/E

题目大意:给一个序列a1~an(n<=1000000),问对于每一个ai来说,是否存在一个数x使得 ai&x==0且x是序列a 中的元素。ai的范围是(1<=ai<=4000000)。

思路:对于一个数,比如说53,其二进制表示为 110101,取它的反,即为001010,我们知道,若一个数x使得x&53==0,则x的二进制一定是这样一个形式:00?0?0(其中?表示可以为0也可以为1)。那么我们设这样一个数组 vis[x]={0,1},表示x是否可以由序列a中的元素,将其二进制中的若干0改为1后得到。比如说如果a中存在53(110101),那么我们可以得到 (111101)61,(110111)55,(111111)63,则vis[53]=vis[55]=vis[61]=vis[63]=1,这个数列我们可以先预处理一遍得到,因为ai最大为4000000,所以预处理的复杂度为 (1<<K)*K(其中K为序列中最大的数其二进制表示的长度,最大为22)。那么有了这个数组,我们就可以求出对于每一个数的答案了,首先,我们要求ai对应的数,我们首先取反,得到tmp,此时若vis[tmp]==0,我们可以肯定序列a中不存在与ai与操作后等于0的数,直接输出-1,否则,我们可以知道一定存在一个数,那么我们怎么求这个数呢?

具体步骤如下:

我们首先设答案ans为0,我们枚举tmp的每一位,如果遇到1,则我们把这一位设为0后,再看tmp是否存在,如果不存在,说明我们要找的那个数这一位上一定为1,则我们将ans的这一位赋值为1,然后将tmp恢复后继续遍历,如果存在,说明我们要找的数这一位可以为0(注意不是一定),则我们继续遍历即可。如果遇到0,直接继续遍历,因为我们要找的数这一位上一定为0。遍历完之后,ans的值即为答案,输出即可。这一步的时间复杂度为O(n*K),(K由上面定义),可以在规定的时间内跑完。具体代码如下:

#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#define ll long long
#define maxn 4500000
using namespace std;
int getlen(int x)
{
    int sum=0;
    while(x)
    {
        sum++;
        x/=2;
    }
    return sum;
}
int vis[maxn];
int a[1000010];
int main()
{
    //freopen("dd.txt","r",stdin);
    memset(vis,0,sizeof(vis));
    int n,max=0,i,j;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(max<a[i])
        max=a[i];
        vis[a[i]]=1;
    }
    if(n==1)
   {
       printf("-1\n");
       return 0;
   }

    int len=getlen(max);
    int limit=(1<<len)-1;
    for(i=0;i<=limit;i++)
    {
        if(!vis[i])
        continue;
        for(j=0;j<len;j++)
        {
            if(((1<<j)&i)==0)
            {
                vis[i^(1<<j)]=1;
            }
        }
    }
    for(i=1;i<=n;i++)
    {
        int tmp=limit-a[i];
        //printf("%d\n",tmp);
        if(!vis[tmp])
        printf("-1 ");
        else
        {
            int ans=0;
            for(j=0;j<len;j++)
            {
                if(tmp&(1<<j))
                {
                    if(vis[tmp^(1<<j)])
                    tmp^=(1<<j);
                    else
                    {
                        ans+=1<<j;
                    }
                }
            }
            printf("%d ",ans);
        }
    }
    printf("\n");
    return 0;
}


 

原文地址:https://www.cnblogs.com/xinyuyuanm/p/3000809.html