给定非负整数 n,x,y,对于所有满足 i=1∑nai=x,所有 ai 按位或和为 y 的序列 a,求出 ⊕i=1nai 的异或和。
1≤n≤240,0≤x<260,0≤x<220。
不难发现由于可以任意调换位置,所以所有 ai 本质相同,也就是说,n 为偶数时答案为 0。
考虑 n 为奇数的情况,此时只需要统计所有 a1 的异或和即可。
考虑拆位,对每个 i 计算 a1 第 i 位为 1 的方案数 mod 2 的结果 cnti,则答案即为 ∑2icnti。
考虑通过容斥计算 cnti,设 f(y′) 表示 ai 的或和为 y′ 的子集且 a1 第 i 位为 1 的方案数,则:
cnti=⊕y′⊆y(−1)∣y∣−∣y′∣f(y′)=⊕y′⊆yf(y′)
考虑计算 f(y′),由于 a1 第 i 位一定为 1,所以不妨令 a1→a1−yi,则有:
f(y′)=a1+a2+⋯+an=x−2i∑[a1⊆(y′−2i)]i=2∏n[ai⊆y′]mod2
注意到 (mn) mod 2=[m⊆n],所以:
f(y′)=a1+a2+⋯+an=x−2i∑(a1y′−2i)i=2∏n(aiy′)mod2=(x−2iny′−2i)mod2=[(x−2i)⊆(ny′−2i)]
所以答案为:
∑2i⊕y′⊆y[(x−2i)⊆(ny′−2i)]
时间复杂度 O(ylogy),代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
long long n,x;
int y;
int main()
{
scanf("%lld%lld%d",&n,&x,&y);
if(n&1^1) return puts("0"),0;
int ans=0;
for(int i=0;i<20;i++)
{
int f=0;
for(int j=y;j>0;j=(j-1)&y)
{
if(j>>i&1^1) continue;
f^=((x-(1<<i))|(n*j-(1<<i)))==(n*j-(1<<i));
}
ans+=f<<i;
}
printf("%d\n",ans);
return 0;
}