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;
}
}