您的位置:首页 > 其它

codeforces 165E - Compatible Numbers

2013-04-05 14:52 351 查看
昨天看到一道好题,现与大家分享一下。

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: