Gushing over Pr|

BigSmall_En

园龄:3年2个月粉丝:3关注:5

2022-08-29 16:41阅读: 65评论: 0推荐: 0

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 中国大陆许可协议进行许可。

posted @   BigSmall_En  阅读(65)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起