题解 CF1119H 【Triple】
command_block · · 题解
前置芝士:位运算卷积FWT
或许有用的文章
约定:
这题是真的妙啊! Orz出题人!!!
如果你熟悉生成函数卷积计数那一套,那么容易得到 :
把一个三元组
然后把所有幂级数都异或卷积起来就能得到答案了。
可是这题值域
每个幂级数只有3个非零值,我们考虑找找规律。
设
即
由于
众所周知
(
则
推导到这里,如果按照上式暴力算出每个幂级数的“点值”然后点乘,再
形式化地讲:
那么
后面的乘积项一共只有八种可能(正负号)
我们分别求出这八种可能的个数就能用快速幂求答案了。
八种可能太烦了,有一种巧妙的化简方法 :
由异或的自反性,每个把三元组
那么最后的结果异或上
反映到多项式上就是做一个下标映射。
回到这个式子:
我们现在经过改造之后所有的
那么就只剩四种情况 :
设这四种情况的方案数分别是
我们求出这些
我们考虑弄出
显然有
- 如果仅令
F_k[b_k]=1 ,即x=0,y=1,z=0
那么此时
这样就只考虑
我们有
( 因为这东西是一个线性变换,其实就是向量乘矩阵 )
那么我们计算
- 如果令
F_k[c_k]=1 ,即x=0,y=0,z=1
那么此时
只考虑
同样是算
- 如果只令
F_k[b_k⊕c_k]=1
那么就相当于把上面两种情况卷积,也就是点值点积。
那么此时
那么
(同时考虑
现在
具体的方程是:
原式
解得
注意这里不用考虑取模问题,因为
最后再
复杂度
#include<algorithm>
#include<cstdio>
#define Maxn 140000
#define mod 998244353
#define inv2 499122177ll
#define ll long long
using namespace std;
inline int read()
{
register int X=0;
register char ch=0;
while(ch<48||ch>57)ch=getchar();
while(ch>=48&&ch<=57)X=X*10+(ch^48),ch=getchar();
return X;
}
ll powM(ll a,ll t=mod-2)
{
ll ans=1;
while(t){
if(t&1)ans=ans*a%mod;
a=a*a%mod;
t>>=1;
}return ans;
}
int m;
ll x,y,z;
ll f1[Maxn],f2[Maxn],f3[Maxn],f[Maxn];
void DWT(ll *a)
{
for (int len=1;len<m;len<<=1)
for (int p=0;p<m;p+=len+len)
for (int i=p;i<p+len;i++){
ll sav0=a[i];
a[i]+=a[i+len];
a[i+len]=sav0-a[i+len];
}
}
void IDWT(ll *a)
{
for (int len=1;len<m;len<<=1)
for (int p=0;p<m;p+=len+len)
for (int i=p;i<p+len;i++){
ll sav0=a[i];
a[i]=(a[i]+a[i+len])*inv2%mod;
a[i+len]=(sav0-a[i+len])*inv2%mod;
}
}
int n;
int main()
{
n=read();m=(1<<read());
x=read();y=read();z=read();
int sav=0;
for (int i=0,a,b,c;i<n;i++){
a=read();b=read();c=read();
sav^=a;b^=a;c^=a;
f1[b]++;f2[c]++;f3[b^c]++;
}
DWT(f1);DWT(f2);DWT(f3);
ll s1=(x+y+z)%mod,s2=(x+y-z)%mod,
s3=(x-y+z)%mod,s4=(x-y-z)%mod;
for (int i=0;i<m;i++){
ll c1=(n+f1[i]+f2[i]+f3[i])/4;
f[i]=powM(s1,c1)*
powM(s2,(n+f1[i]-c1-c1)/2)%mod*
powM(s3,(n+f2[i]-c1-c1)/2)%mod*
powM(s4,(n+f3[i]-c1-c1)/2)%mod;
}IDWT(f);
for (int i=0;i<m;i++)
printf("%lld ",(f[i^sav]+mod)%mod);
return 0;
}