【题目】
CF
给出三个数
x
,
y
,
z
x,y,z
x,y,z,再给出
n
n
n组数,每组数包含
(
x
+
y
+
z
)
(x+y+z)
(x+y+z)个数,
x
x
x个
a
a
a,
y
y
y个
b
b
b,
z
z
z个
c
c
c,那么从每一组数中选择一个数的异或值为
t
t
t的方案数是多少,对每个
t
t
t输出答案对
998244353
998244353
998244353取模
n
≤
1
0
5
,
k
≤
2
1
7
,
a
,
b
,
c
<
2
k
n\leq 10^5,k\leq 2^17,a,b,c< 2^k
n≤105,k≤217,a,b,c<2k
【解题思路】
高妙!
一种暴力的方法是,对于第
i
i
i组,我们令
f
i
,
a
i
=
x
,
f
i
,
b
i
=
y
,
f
i
,
c
i
=
z
f_{i,a_i}=x,f_{i,b_i}=y,f_{i,c_i}=z
fi,ai=x,fi,bi=y,fi,ci=z,其余为
0
0
0,然后
n
n
n个多项式
FWT
\text{FWT}
FWT点积再逆变换就可以得到答案了。
要优化这个暴力,由于只有三个值进行
FWT
\text{FWT}
FWT,那么对于每个位置的贡献情况只有
8
8
8种,仍然不好考虑。
但我们发现,若将序列整体异或
a
i
a_i
ai,即
f
i
,
0
=
x
,
f
i
,
a
i
⊕
b
i
=
y
,
f
i
,
a
i
⊕
c
i
=
z
f_{i,0}=x,f_{i,a_i\oplus b_i}=y,f_{i,a_i\oplus c_i}=z
fi,0=x,fi,ai⊕bi=y,fi,ai⊕ci=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,x−y−z。
考虑每一个位置
i
(
0
≤
i
<
2
k
)
i(0\leq i<2^k)
i(0≤i<2k),那么就是上面四个贡献的若干次方的乘积,不妨设次方分别为
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d。
那么我们现在可以得到一个简单的柿子
a
+
b
+
c
+
d
=
n
a+b+c+d=n
a+b+c+d=n。考虑再构造出其他的方程使得我们能解出这个东西,于是可以用
x
x
x和
y
y
y同号的方案数减去
x
x
x和
y
y
y异号的方案数,即仅对
f
a
i
⊕
b
i
=
1
f_{a_i\oplus b_i}=1
fai⊕bi=1进行
FWT
\text{FWT}
FWT,我们可以得到另一条柿子:
a
+
b
−
c
−
d
=
c
n
t
1
a+b-c-d=cnt_1
a+b−c−d=cnt1。同理再分别对
x
,
z
x,z
x,z和
y
,
z
y,z
y,z做
FWT
\text{FWT}
FWT,就可以得到:
a
−
b
+
c
−
d
=
c
n
t
2
,
a
−
b
−
c
+
d
−
c
n
t
3
a-b+c-d=cnt_2,a-b-c+d-cnt_3
a−b+c−d=cnt2,a−b−c+d−cnt3。
联立解方程组即可。
复杂度 O ( k ⋅ 2 k ) O(k\cdot 2^k) O(k⋅2k)
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=262333,mod=998244353,inv2=(mod+1)>>1;
int n,k,X,Y,Z,inv4,tmp;
int ans[N],A[N],B[N],C[N];
int read()
{
int ret=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
return ret;
}
int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
void up(int &x,int y){x=upm(x+y);}
int mul(int x,int y){return 1ll*x*y%mod;}
int qpow(int x,int y){int res=1;for(;y;y>>=1,x=mul(x,x))if(y&1)res=mul(res,x);return res;}
void fwtxor(int *a,int n,int op)
{
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;++k)
{
ll x=a[j+k],y=a[i+j+k];
a[j+k]=upm(x+y);a[i+j+k]=upm(x-y);
if(!~op) a[j+k]=mul(a[j+k],inv2),a[i+j+k]=mul(a[i+j+k],inv2);
}
}
int main()
{
#ifdef Durant_Lee
freopen("CF1119H.in","r",stdin);
freopen("CF1119H.out","w",stdout);
#endif
n=read();k=read();X=read();Y=read();Z=read();inv4=qpow(4,mod-2);
for(int i=1;i<=n;++i)
{
int a=read(),b=read(),c=read();
tmp^=a;A[a^b]++;B[a^c]++;C[b^c]++;
}
fwtxor(A,1<<k,1);fwtxor(B,1<<k,1);fwtxor(C,1<<k,1);
//for(int i=0;i<(1<<k);++i) printf("%d ",C[i]); puts("");
for(int i=0;i<(1<<k);++i)
{
//printf("%d %d %d\n",A[i],B[i],C[i]);
ll a=upm(((ll)n+A[i]+B[i]+C[i])%mod*inv4%mod),b=upm(((ll)n+A[i]-B[i]-C[i])%mod*inv4%mod);
ll c=upm(((ll)n-A[i]+B[i]-C[i])%mod*inv4%mod),d=upm(((ll)n-A[i]-B[i]+C[i])%mod*inv4%mod);
//printf("%lld %lld %lld %lld\n",a,b,c,d);
ans[i]=1ll*qpow(((ll)X+Y+Z)%mod,a)*qpow(((ll)X+Y-Z)%mod,b)%mod*qpow(((ll)X-Y+Z)%mod,c)%mod*qpow(((ll)X-Y-Z)%mod,d)%mod;
ans[i]=upm(ans[i]);
}
fwtxor(ans,1<<k,-1);
for(int i=0;i<(1<<k);++i) printf("%d ",ans[i^tmp]);
return 0;
}