题目大意
给定一个正整数集合
S
与正整数
- 对于集合中的任意两个不同元素 A 和A&B>0;
- 对于集合中的任意三个不同元素
A 、 B 、C ,A&B&C=0。
其中,&是二进制下的按位与运算。假如满足要求的集合不存在,输出-1;否则,假如有多个解,输出将元素排序后字典序最小的一个。
题解
乱搞构造。
将已经给出来的数,转化为二进制,然后列出来。如15,20
000110111001
如果我们要在下面构造一个新的数,为了满足条件1,我们要使新的数,从每一行选一个1,复制到新的数来,比如乱搞一个:
00−001−010−111−110−001−0
但是,这种情况不符合条件2,如果把这三个数&起来,第三位将是1,所以我们必须保证每一列的1的个数不能超过2。
那就这样构造吧:
00−001−010−111−010−001−1
如果这时候还要添加1个怎么办呢?刚刚我们添加的那一排数,已经没有1可以复制到下一排了,因为那两列的1的个数已经达到2了。
我们只能在刚刚构造的数上再添加1,来保证后面数的可以&它。
我们只能在没有1的列来添加这个新的1,才能使后面数的可以复制这个1。
由于后面每个数都要消耗这个数一个1,所以我们要在这个数上添加x个1(x为后面还要构造的数的个数)
在这个样例,我们还要构造一个数,所以此时只需要在第3行第6列变为1。
00−101−010−111−010−001−1
这样下一个数就有办法构造了。
001−1010−1101−0110−0100−1011−0
如果构造不出来,比如给定的数没有可用的1了,或没有空间添加更多的1了,那就无解,输出-1。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const long long MAXN=205,MAXB=61,MAXBIT=(1LL<<60LL);
int n,m;
long long S[MAXN];
int cnt[MAXB];
int main()
{
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%I64d",S+i);
for(long long bit=1LL,j=0;bit<MAXBIT;bit<<=1LL,j++)
if(S[i]&bit)
cnt[j]++;
}
scanf("%d",&n);
bool flag=true;
for(int i=1;i<MAXB&&flag;i++)
if(cnt[i]>2)
flag=false;
for(int i=1;i<=m&&flag;i++)
for(int j=1;j<=m&&flag;j++)
if((S[i]&S[j])==0LL)
flag=false;
if(flag)
{
long long s;
int cnt1;
for(int i=m+1;i<=n;i++)
{
s=0LL;
for(int j=1;j<i;j++)
{
for(long long bit=1LL,k=0;bit<MAXBIT;bit<<=1LL,k++)
if((S[j]&bit)&&cnt[k]==1)
{
s|=bit;
cnt[k]++;
break;
}
if((s&(S[j]))==0)
{
flag=false;
break;
}
}
if(!flag)break;
cnt1=n-i;
for(long long bit=1LL,k=0;bit<MAXBIT&&cnt1;bit<<=1LL,k++)
if(cnt[k]==0)
{
s|=bit;
cnt[k]++;
cnt1--;
}
if(cnt1)
{
flag=false;
break;
}
S[i]=s;
}
if(flag)
{
sort(S+1,S+n+1);
for(int i=1;i<n;i++)
printf("%I64d ",S[i]);
printf("%I64d\n",S[n]);
}
}
if(!flag)
printf("-1\n");
fclose(stdin);
fclose(stdout);
return 0;
}