CF1616H Keep XOR low

感觉是一道比较具有启发性的异或计数题。

考虑存在一个比较显然的想法就是我们通过高位来分组。我们考虑在当前位 \(i\) 时分成两组 \(A,B\) ,表示我们至少要在 \(A,B\) 两组各中选择一个,以满足 \(i+1\) 位及以上的上界被填满。

然后我们便根据 \(x\) 的当前位来分类,首先我们可以将 \(A,B\) 再根据当前位的 \(01\) 再分成 \(A_0,A_1,B_0,B_1\) 四组 。

  • 如果 \(x\) 的当前位是 \(0\) ,那么显然的,这一位就只能是 \(0\) ,我们直接递归 \((A_0,B_0),(A_1,B_1)\) 即可。
  • 如果 \(x\) 的当前位是 \(1\) ,这一位就既可以是 \(0\) ,也可以是 \(1\) 。首先如果选择该位是 \(0\) 的话,相当于后面的位置便没有了限制,我们直接乘上一个组合数还是二的幂次即可。如果选择该位置是 \(1\) 的话,我们实际上至少让 \((A_0,B_1)\)\((A_1,B_0)\) 至少被选一对,剩下任意即可。

我们再考虑如何在初始的时候找到这个 \(A,B\) ,可以发现,前导零是不能让我们将所有数分组的,因为他不能告诉我们要强制选择两个组中的哪些个,所以找到最高位的 \(1\) ,利用其进行分组即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1.5e5+5;
const int MOD=998244353;
int ADD(int x,int y){return x+y>=MOD?x+y-MOD:x+y;}
int TIME(int x,int y){return (int)(1ll*x*y%MOD);}
int n,x,a[N],ksm[N];
int solve(int k,vector<int> A,vector<int> B){
	if(A.empty()||B.empty()) return 0;
	if(k<0) return TIME(ksm[(int)A.size()]-1,ksm[(int)B.size()]-1);
	vector<int> A0,A1,B0,B1;
	for(int i=0;i<(int)A.size();++i){
		if((A[i]>>k)&1) A1.push_back(A[i]);
		else A0.push_back(A[i]);
	}
	for(int i=0;i<(int)B.size();++i){
		if((B[i]>>k)&1) B1.push_back(B[i]);
		else B0.push_back(B[i]);
	}
	if(!((x>>k)&1)) return ADD(solve(k-1,A0,B0),solve(k-1,A1,B1));
	int tmp1=solve(k-1,A0,B1),tmp2=solve(k-1,A1,B0),res=0;
	res=ADD(res,TIME(ksm[(int)A0.size()]-1,ksm[(int)B0.size()]-1));
	res=ADD(res,TIME(ksm[(int)A1.size()]-1,ksm[(int)B1.size()]-1));
	res=ADD(res,ADD(tmp1,tmp2));
	res=ADD(res,TIME(tmp1,ADD(ksm[(int)A1.size()]-1,ksm[(int)B0.size()]-1)));
	res=ADD(res,TIME(tmp2,ADD(ksm[(int)A0.size()]-1,ksm[(int)B1.size()]-1)));
	return res=ADD(res,TIME(tmp1,tmp2));
}
int main(){
	cin>>n>>x,ksm[0]=1;
	for(int i=1;i<=n;++i) ksm[i]=TIME(ksm[i-1],2);
	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
	if(!x){
		map<int,int> mp;int res=0;
		for(int i=1;i<=n;++i) mp[a[i]]++;
		map<int,int>::iterator tmp;
		for(tmp=mp.begin();tmp!=mp.end();++tmp)
			res=ADD(res,ksm[tmp->second]-1);
		return printf("%d\n",res),0;
	}
	for(int k=29;k>=0;--k){
		if(!((x>>k)&1)) continue;
		vector<int> A,B;int res=0;
		for(int i=1;i<=n;++i){
			if((a[i]>>k)&1) A.push_back(a[i]);
			else B.push_back(a[i]);
		}
		res=ADD(res,ksm[(int)A.size()]-1);
		res=ADD(res,ksm[(int)B.size()]-1);
		res=ADD(res,solve(k-1,A,B));
		return printf("%d\n",res),0;
	}
}
posted @ 2021-12-31 12:03  Point_King  阅读(153)  评论(2)    收藏  举报