【题面】
【解题思路】
以下的数均已转为二进制数
AND:贪心,从高位往低位,如果集合里的数有这一位的个数小于2,说明答案里不可能包含这一位,继续做下一位的操作;如果个数大于2,显然取有这一位的数会比其他数更优,把其他数从集合中删除。
XOR:贪心,建一棵字典树,同样是尽量使高位为1
OR:递推,设f[x](x为压缩状态)为是否有数包含x集合,显然对于f[x]=true有f[y](y为x的子集)=true,那么我们就可以从大到小递推,每个状态只要更新比它少一个1的状态就可以了(PS:我的程序里f数组顺便记录了权值,与此处略有不同)
【呆马】
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N=1e5+1,M=2100001;
int T,n,m,c,l,L,t,sum,ans,i,j,k,a[N],ls[M],rs[M],f[M],ex[21];
bool cmp(int x,int y){return x>y;}
void add(int x,int y,int b)
{
if (!b) return;
int t=y&ex[b];
if (!t)
{
if (!ls[x]) ls[x]=++m;
add(ls[x],y,b-1);
}
else
{
if (!rs[x]) rs[x]=++m;
add(rs[x],y,b-1);
}
}
void find(int x,int y,int b)
{
if (!b) return;
int t=y&ex[b];
if (!t)
{
if (rs[x])
{
sum+=ex[b];
find(rs[x],y,b-1);
}
else if (ls[x]) find(ls[x],y,b-1);
}
else
{
if (ls[x])
{
sum+=ex[b];
find(ls[x],y,b-1);
}
else if (rs[x]) find(rs[x],y,b-1);
}
}
int main()
{
ex[1]=1;
for (i=2;i<=21;i++) ex[i]=ex[i-1]<<1;
for (scanf("%d\n",&T);T;T--,scanf("\n"))
{
scanf("%d%d\n",&n,&c);
l=1;
for (i=1;i<=n;i++)
{
scanf("%d",&a[i]);
for (;a[i]>=ex[l+1];l++);
}
L=ex[l+1]-1;
ans=0;
if (c==1)
{
sort(a+1,a+n+1,cmp);
t=n;
for (i=l;i;i--)
{
m=0;
for (j=1;j<=t;j++)
if (a[j]&ex[i]) m++;
if (m<2) continue;
for (j=1;j<=t;j++)
for (;j<=t && !(a[j]&ex[i]);) swap(a[j],a[t--]);
}
printf("%d\n",a[1]&a[2]);
}
if (c==2)
{
m=1,ans=0;
for (i=1;i<=n;i++) add(1,a[i],l);
for (i=1;i<=n;i++)
{
sum=0;
find(1,a[i],l);
ans=max(ans,sum);
}
for (i=1;i<=m;i++) ls[i]=rs[i]=0;
printf("%d\n",ans);
}
if (c==3)
{
sort(a+1,a+n+1,cmp);
for (i=1;i<=L;i++) f[i]=0;
for (i=a[1],j=1;i;i--)
{
for (;j<=n && a[j]==i;j++) f[i]=i;
if (f[i])
for (k=1;k<=l;k++)
{
t=i&ex[k];
if (t) f[i-t]=i-t;
}
}
for (i=1;i<L;i++)
{
for (j=1;j<=l;j++)
if (!(i&ex[j])) f[i|ex[j]]=max(f[i|ex[j]],f[i]);
}
for (i=1;i<=n;i++) ans=max(ans,a[i]+f[L-a[i]]);
printf("%d\n",ans);
}
}
}