ARC156D 题解
相当妙的题。
异或有个很好的性质,就是异或两次会抵消。这样我们只需要考虑出现次数为奇数的方案即可。
我们设 \(t_i\) 表示 \(a_i\) 的出现次数。那么这种方案的和 \(sum=\sum_{i=1}^{n}a_it_i\),出现次数为
可以这么理解:先把前 \(i-1\) 个数选了,然后考虑 \(a_i\) 放在哪些位置。
由于我们要求其 \(\bmod 2\) 的值是否为 \(1\),而只有一堆 \(1\) 乘起来才是 \(1\),所以只有当每个组合数都是 \(1\) 的时候结果才是 \(1\)。现在问题转化为:给你一堆组合数,判断是否每个组合数对 \(2\) 取模的值都是 \(1\)。
由 Lucas 定理,可得 \(\binom{n}{m}\bmod 2=1\) 当且仅当不存在任何一个 \(i\),使得 \(m\) 的第 \(i\) 位为 \(1\) 而 \(n\) 的第 \(i\) 位为 \(0\)。换句话说,\(m\) 的二进制表示是 \(n\) 的二进制表示的子集。
再代回原式,\(t_1\) 是 \(k\) 的子集,\(t_2\) 是 \(k-t_1\) 的子集……我们会发现 \(t_1,...t_n\) 构成了 \(k\) 的一个划分!换句话说,对于 \(k\) 二进制下的每一位 \(1\),其只能分配给某一个 \(t_i\)。
然后我们逐位计算异或和。考虑数位 dp,设 \(f_{i,j}\) 表示从低到高考虑到第 \(i\) 位,\(j\) 为 \(sum\) 截掉 \(i-1\) 及更低位之后的值(也就是 \(sum\) 除以 \(2^{i-1}\) 之后的值)。因为前 \(i-1\) 位都计算过了,就只需要考虑后面的位。
初始有 \(f_{0,0}=1\)。然后考虑转移。假设枚举到第 \(i\) 位(\(0\sim60\),不是 \(k\) 的每一位),\(k\) 在第 \(i\) 位上的值为 \(up\),那么有:
-
\(f_{i-1,j}=0\)。方案数如果已经是偶数了那之后再怎么乘还是偶数,跳过。
-
\(up=0\),\(f_{i,\lfloor\frac{j}{2}\rfloor}\leftarrow f_{i,\lfloor\frac{j}{2}\rfloor}\oplus f_{i,j}\) ,即 \(f_{i,\lfloor\frac{j}{2}\rfloor}\leftarrow f_{i,\lfloor\frac{j}{2}\rfloor}\oplus 1\)
-
\(up=1\),枚举这一位选什么。设选了 \(a_p\),那么 \(f_{i,\lfloor\frac{j+a_p}{2}\rfloor}\leftarrow f_{i,\lfloor\frac{j+a_p}{2}\rfloor}\oplus f_{i,j}\) ,即 \(f_{i,\lfloor\frac{j+a_p}{2}\rfloor}\leftarrow f_{i,\lfloor\frac{j+a_p}{2}\rfloor}\oplus 1\)。
最后在 dp 过程中统计答案。
第 \(i\) 位的贡献不仅要考虑 \(f_{i,j}\),还要考虑后面没选的位的方案数。设 \(k\) 一共有 \(M\) 位 \(1\),当前考虑了前 \(m\) 个 \(1\),则后面的位的方案数为 \(n^{M-m}\)。显然这玩意只有当 \(n\) 为奇数或者 \(M=m\) 的时候才是奇数,会对答案产生贡献。
第一个条件容易判断,第二个条件可以记录一下 \(k\) 最高的 \(1\) 是哪一位。假设为 \(high\),那么只有当此时的 \(i\ge high\) 时满足条件。最后还要判断 \(sum\) 的第 \(i\) 位是否为 \(1\),直接查 \(j/j+a_p\) 的最低位即可。如果都满足条件,就给答案异或上 \(2^i\)。
时间复杂度 \(O(n\max a_i\log k)\),因为 \(j\) 的上界只有 \(\max a_i\)。空间容易通过滚动数组优化到 \(O(n+\max a_i)\),但是注意 \(j\) 还要加上个 \(a_p\) 所以 dp 数组要开到 \(2\max a_i\)。
代码:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define gt getchar
#define pt putchar
#define fst first
#define scd second
typedef long long ll;
const int N=2005;
using namespace std;
using namespace __gnu_pbds;
typedef pair<int,int> pii;
inline bool __(char ch){return ch>=48&&ch<=57;}
template<class T> inline void read(T &x){
x=0;bool sgn=0;char ch=gt();
while(!__(ch)&&ch!=EOF) sgn|=(ch=='-'),ch=gt();
while(__(ch)) x=(x<<1)+(x<<3)+(ch&15),ch=gt();
if(sgn) x=-x;
}
template<class T,class ...T1> inline void read(T &x,T1 &...x1){
read(x);
read(x1...);
}
template<class T> inline void print(T x){
static char st[70];short top=0;
if(x<0) pt('-');
do{st[++top]=x>=0?(x%10+48):(-(x%10)+48),x/=10;}while(x);
while(top) pt(st[top--]);
}
template<class T> inline void printsp(T x){
print(x);
putchar(' ');
}
template<class T> inline void println(T x){
print(x);
putchar('\n');
}
int n,a[N],high;
ll k,ans;
bool f[N],g[N];
signed main(){
read(n,k);
for(int i=1;i<=n;++i) read(a[i]);
high=60;
while(!((k>>high)&1)) high--;
f[0]=1;
for(int x=0;x<=60;++x){
memset(g,0,sizeof(g));
int up=(k>>x)&1;
for(int val1=0;val1<=1000;++val1){
if(!f[val1]) continue;
if(!up){
if(((n%2)||(x>=high))&&(val1&1)) ans^=(1ll<<x);
g[val1>>1]^=1;
}else{
for(int i=1;i<=n;++i){
int val2=val1+a[i];
if(((n%2)||(x>=high))&&(val2&1)) ans^=(1ll<<x);
g[val2>>1]^=1;
}
}
}
memcpy(f,g,sizeof(f));
}
println(ans);
return 0;
}