模板题:https://www.luogu.org/problemnew/show/P4717
做法,类似ntt,去掉刚开始的那个swap到某一位置,然后在把变换改成如下操作:
And:正变换A[i+j]+=A[i+j+md],逆的A[i+j]-=A[i+j+md] (与前加减后)
Or:正变换A[i+j+md]+=A[i+j],逆的A[i+j+md]-=A[i+j+md] (或后加减前)
Xor:正变换 A'[i+j+md]=A[i+j]-A[i+j+md],A'[i+j]=A[i+j]+A[i+j+md] ,逆的*1/2(异或最像fft)
再说一下每个变换的意义,And正变换相当于将每个位置的值加到这个位置所有的子集位置上去(也可以说每个位置的值加上所有超集[所有包含它的集合]的值),逆变换相当于每个位置减到(下传减法)这个位置的所有子集位置上去(每个位置的值减去所有超集位置上的值)。Or正变换相当于将每个位置的值加到这个位置的超集位置上去(每个位置的值加上子集位置的值),逆变换相当于将每个位置的值减到这个位置的超集位置上去(每个位置的值减去所有子集位置的值)。Xor么。。。真不知道啥意义,有大佬知道可以说下。
所以快速沃尔什变换不仅仅是正变换与逆变换组合在一起用,拆开来也是很有用的集合操作。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=998244353;
int cc,len=1;
ll A[1<<17],B[1<<17],C[1<<17],D[1<<17],inv2;
void Ad(ll &x,ll y)
{x=(x+y)%mod;}
void Dw(ll &x,ll y)
{x=(x-y+mod)%mod;}
void Mul(ll &x,ll y)
{x=x*y%mod;}
ll qpow(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod,y>>=1;
}
return res;
}
void And(ll *a,bool inv)
{
for(int l=2,md=1;l<=len;l<<=1,md<<=1)
for(int i=0;i<len;i+=l)
for(int j=0;j<md;j++)
inv?Dw(a[i+j],a[i+j+md]):Ad(a[i+j],a[i+j+md]);
}
void Or(ll *a,bool inv)
{
for(int l=2,md=1;l<=len;l<<=1,md<<=1)
for(int i=0;i<len;i+=l)
for(int j=0;j<md;j++)
inv?Dw(a[i+j+md],a[i+j]):Ad(a[i+j+md],a[i+j]);
}
void Xor(ll *a,bool inv)
{
ll tp;
for(int l=2,md=1;l<=len;l<<=1,md<<=1)
for(int i=0;i<len;i+=l)
for(int j=0;j<md;j++)
{
tp=a[i+j+md];
a[i+j+md]=(a[i+j]-tp+mod)%mod;
Ad(a[i+j],tp);
if(inv)Mul(a[i+j],inv2),Mul(a[i+j+md],inv2);
}
}
void sol(int op,ll *a,bool inv)
{
if(op==0)Or(a,inv);
if(op==1)And(a,inv);
if(op==2)Xor(a,inv);
}
int main()
{
inv2=qpow(2,mod-2);
scanf("%d",&cc);
len=pow(2,cc);
for(int i=0;i<len;i++)
scanf("%lld",&A[i]);
for(int i=0;i<len;i++)
scanf("%lld",&B[i]);
for(int i=0;i<=2;i++)
{
memcpy(C,A,sizeof A),memcpy(D,B,sizeof B);
sol(i,C,0),sol(i,D,0);
for(int j=0;j<len;j++)Mul(C[j],D[j]);
sol(i,C,1);
for(int j=0;j<len;j++)printf("%lld ",C[j]);
puts("");
}
}