题面
题意
给出三个数x,y,z,再给出n组数,每组数包含(x+y+z)个数,x个a,y个b,z个c,那么从每一组数中选择一个数的异或值为t的方案数是多少,对每个t输出答案,a,b,c均小于(1<<m).
做法
首先考虑一个简单的暴力,对每组数进行一次fwt,然后全部乘起来,时间复杂度为
O
(
2
m
∗
m
∗
n
)
O(2^m*m*n)
O(2m∗m∗n),肯定不行.
对于一组数a,b,c,可以考虑对数组中的所有位置均异或上a,这样只要最后的数组的所有位置再异或上a,就能使答案不变,而且经过这样的转化后a,b,c就可以看作是0,a^ b,a^c,可以发现,对这组数进行fwt后的数组中只有
x
+
y
+
z
,
x
+
y
−
z
,
x
−
y
+
z
,
x
−
y
−
z
x+y+z,x+y-z,x-y+z,x-y-z
x+y+z,x+y−z,x−y+z,x−y−z四种权值.
考虑fwt之后相乘,每一位的权值,都可以看作是
(
x
+
y
+
z
)
c
1
∗
(
x
+
y
−
z
)
c
2
∗
(
x
−
y
+
z
)
c
3
∗
(
x
−
y
−
z
)
c
4
(x+y+z)^{c1}*(x+y-z)^{c2}*(x-y+z)^{c3}*(x-y-z)^{c4}
(x+y+z)c1∗(x+y−z)c2∗(x−y+z)c3∗(x−y−z)c4显然满足
c
1
+
c
2
+
c
3
+
c
4
=
n
c1+c2+c3+c4=n
c1+c2+c3+c4=n,为了对每一位都求出这四个数,就要考虑构造其他方程.
可以考虑x和y同号的方案数减去x和y异号的方案数,这个值恰好为对数组
A
[
a
A[a
A[a^
b
]
b]
b]++(每一组数的a,b)做一次fwt后的值,因此对第i位可得
c
1
+
c
2
−
c
3
−
c
4
=
A
[
i
]
c1+c2-c3-c4=A[i]
c1+c2−c3−c4=A[i].
对于x和z,y和z也可如此,这样一共就能得到四个方程,凭此解出
c
1
,
c
2
,
c
3
,
c
4
c1,c2,c3,c4
c1,c2,c3,c4即可.
代码
#include<bits/stdc++.h>
#define ll long long
#define N 150000
#define M 998244353
using namespace std;
ll n,m,x,y,z,er,si,tmp,A[N],B[N],C[N],ans[N];
inline ll po(ll u,ll v)
{
ll res=1;
for(;v;)
{
if(v&1) res=res*u%M;
u=u*u%M;
v>>=1;
}
return res;
}
inline void fwt(ll *a,bool dft)
{
ll i,j,k,x,y;
for(i=1;i<(1 << m);i<<=1)
{
for(j=0;j<(1 << m);j+=(i<<1))
{
for(k=j;k<i+j;k++)
{
x=a[k],y=a[k+i];
a[k]=(x+y)%M;
a[k+i]=(M+x-y)%M;
if(!dft)
{
a[k]=a[k]*er%M;
a[k+i]=a[k+i]*er%M;
}
}
}
}
}
int main()
{
er=(M+1)/2,si=po(4,M-2);
ll i,j,a,b,c,d;
cin>>n>>m>>x>>y>>z;
for(i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&a,&b,&c);
tmp^=a,A[a^b]++,B[a^c]++,C[b^c]++;
}
fwt(A,1),fwt(B,1),fwt(C,1);
for(i=0;i<(1 << m);i++)
{
a=(n+A[i]+B[i]+C[i])*si%M;
b=(n+A[i]-B[i]-C[i])*si%M;
c=(n-A[i]+B[i]-C[i])*si%M;
d=(n-A[i]-B[i]+C[i])*si%M;
a=(a+M)%M,b=(b+M)%M,c=(c+M)%M,d=(d+M)%M;
ans[i]=po(x+y+z,a)*po(x+y-z,b)%M*po(x-y+z,c)%M*po(x-y-z,d)%M;
ans[i]=(ans[i]+M)%M;
}
fwt(ans,0);
for(i=0;i<(1 << m);i++) printf("%lld ",ans[i^tmp]);
}