CF1721D Maximum AND 题解
CF1721D Maximum AND
给你两个数组 \(a,b\)。定义一个数组 \(c\),其中每个元素 \(c_i=a_i\oplus b_i\)。定义函数 \(f(a,b)=c_1\& c_2\&\cdots \& c_n\)。求一个 \(b\) 的排列 \(b'\),使得 \(f(a,b')\) 的值最大,输出这个最大值。
\(\oplus\) 为按位异或,$& $ 为按位与。
数据范围:\(n\leq 10^5,0\leq a_i,b_i\leq 2^{30}\)。
一个显然的点是 \(a,b\) 的地位是等价的,也可以对 \(a\) 数组进行排列。
最后答案是所有 \(c_i\) 按位与的形式。根据我们处理这类位运算的思想,需要从高位往低位贪心,高位答案能取 \(1\) 就取 \(1\)。
答案的第 \(k\) 位要取 \(1\),其必要条件是所有 \(c_i\) 的这一位取值都要是 \(1\)。同时只有 \(0\oplus 1=1\oplus 0=1\),这也就说明了如果 \(a_i\) 这一位为 \(1\),那么 \(b_i\) 这一位为 \(0\);如果 \(a_i\) 这一位为 \(0\),\(b_i\) 这一位为 \(1\)。最终可以得出 \(a,b\) 中所有数的第 \(k\) 位为 \(1\) 的个数为 \(n\)。
所以在满足第 \(k\) 位可以取 \(1\) 的前提下,我们将 \(a,b\) 重新排序,重排后的 \(a\) 数组第 \(k\) 位为 \(1\) 的在前,为 \(0\) 的在后;\(b\) 数组则反之,\(0\) 在前,\(1\) 在后。这样得到的数组上下异或起来就是满足这一位的与为 \(1\) 的。同时假设重排后 \(a\) 数组 \(k\) 位为 \(0\) 的位置为 \(mid\),那么 \([1,mid-1]\) 和 \([mid,r]\) 这两个区间内的数随意排列也是满足这一位为 \(k\) 这一条件的。
显然这是一个分治的过程。但是如果对于更低的位我们也要取 \(1\),显然此时每一个子区间都要合法。一旦这一位不能取 \(1\),那么这一位的所有子区间都不用再分治了,直接继承到更更低的位即可。
代码实现
赛时代码,及其繁琐,有很大的精简空间。
const int N=100005;
int n,a[N],b[N];bool ans[32];
int tmp;
inline bool cmpa(int x,int y){return ((x>>tmp)&1)>((y>>tmp)&1);}
inline bool cmpb(int x,int y){return ((x>>tmp)&1)<((y>>tmp)&1);}
typedef pair<int,int>ttfa;
ttfa lis[N],que[N];int tot,top;
inline bool check(int loc,int l,int r){
int bota=0,botb=0,len=r-l+1;
for(int i=l;i<=r;++i){
if((a[i]>>loc)&1)++bota;
if((b[i]>>loc)&1)++botb;
}
if(bota+botb!=len)return 0;
return 1;
}
inline void divs(int loc,int l,int r){
//printf("divs %d %d %d\n",loc,l,r);
int bota=0,botb=0;
for(int i=l;i<=r;++i){
if((a[i]>>loc)&1)++bota;
if((b[i]>>loc)&1)++botb;
}
tmp=loc;
sort(a+l,a+r+1,cmpa);
sort(b+l,b+r+1,cmpb);
if(bota!=0)que[++top]={l,l+bota-1};
if(botb!=0)que[++top]={r-botb+1,r};
}
inline void solve(){
n=read();
memset(ans,1,sizeof(ans));
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=n;++i)b[i]=read();
lis[tot=1]={1,n};
for(int k=30;k>=0;--k){
for(int i=1;i<=tot;++i){
if(!check(k,lis[i].first,lis[i].second))
{ans[k]=0;break;}
}
if(ans[k]==0)continue;
top=0;
for(int i=1;i<=tot;++i)
divs(k,lis[i].first,lis[i].second);
tot=top;
for(int i=1;i<=tot;++i)
lis[i]=que[i];
}
int lasans=0;
for(int i=30;i>=0;--i)
if(ans[i])lasans|=(1<<i);
printf("%d\n",lasans);
}
int main(){
int T=read();
while(T--)solve();
return 0;
}
本文作者:BigSmall_En
本文链接:https://www.cnblogs.com/BigSmall-En/p/16636432.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步