快速沃尔什变换学习笔记

89 篇文章 0 订阅
72 篇文章 0 订阅

模板题: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("");
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值