算重学(4) FWT

https://www.cnblogs.com/alex-wei/p/set_power_series.html

https://www.luogu.com.cn/blog/command-block/wei-yun-suan-juan-ji-yu-ji-kuo-zhan

感觉好多人都有点误人子弟了。

高维前缀和

注意到求得是 fS=TSgT,考虑压维直接做就好了。

当然,我们在做这个东西的时候,只是用了加法的结合律、交换律。因此,取 max,min 这些运算显然也可以做。

当然,如果需要差分还原回去的话,要求运算可逆。

https://www.luogu.com.cn/problem/AT_arc100_c

FWT

本质上是做一个线性变换。

c(i,j)ji 的贡献系数,然后一顿分析后只要构造出来的 c(i,j) 满足 c(i,jk)=c(i,j)c(i,k),即可。又因为位运算独立,因此你可以构造出来 c(i,j)=c(i0,j0)c(i1,j1)... 这样的形式,这样你只要满足等式在所有元为 0/1 时都成立即可。记 jk=p,等式即为 c(i0,p0)c(i1,p1)...=c(i0,j0)c(i0,k0)c(i1,j1)c(i1,k1)...=c(i0,j0k0)c(i1,j1k1)...,显然这个构造成立。

接下来,考虑分治,你注意到分治的过程的每一步都是乘上一个矩阵。

然后它这个实现的过程可以考虑当前最高位限制为 n,看看现在序列 f 究竟存的是啥 f(i)=lj<rc(i,j)f(j),iinj,这个东西即为定义式。然后你考虑从上次做完的结构推出来,f(i)=lj<midc(i,j)f(j)+midj<rc(i,j)f(j),然后把 n 恰出去,则你会得到 fn(i)=c(in,0)fn1(i)+c(in,1)fn1(i+2n1)

其实本质上我们理应更强的限制一下计算式,设做规模为 2n 的 FWT,f(i)=0j<2nc(i,j)f(j),那么显然你最高位为 n1,然后你把 n1 提出去,发现规模缩小了一半,即两部都是 0j<2n1c(i,j)f(j),这样不难看出即为 2n1 规模的 FWT 做出来的答案。

然后 IFWT 的过程就是你在每一步变为乘上逆矩阵,但为啥你的方向没有变化!

我也想不明白,但是显然你回溯上去,每一步乘逆矩阵的写法一定是对的!而那些将 FWT,IFWT 整合在一起的,我感觉应该是用了结合律,交换律吧。但我还是推荐大家写开 2 种!

本质上应该都是在做一个线性变换,所以你不把它看成是逆的过程也是没事的,二者都是正确的。

然后边界问题的话,我们的过程是对原先的 f 做线性变换,自然边界也是 f 了。

或者理性点思考,你当最高位限制为 1 时,自然区间长度只有 2,此时每个数只有 1 位,即第 0 位,左边第 0 位为 0,右边 1,然后你考虑定义式 fi=c(0,0)fi+c(0,1)fi+1,自然边界也是这个东西了。

#include <bits/stdc++.h> #define int long long #define pb push_back using namespace std; const int mod=998244353,M=(int)(1e5),N=(1ll<<17)+5; int fpow(int x,int y) { int res=1; x%=mod; while(y) { if(y&1) res=res*x%mod; y>>=1; x=x*x%mod; } return res; } const int inv2=fpow(2,mod-2); const int Cor[2][2]={{1,0},{1,1}}, Cand[2][2]={{1,1},{0,1}}, Cxor[2][2]={{1,1},{1,mod-1}}, ICor[2][2]={{1,0},{mod-1,1}}, ICand[2][2]={{1,mod-1},{0,1}}, ICxor[2][2]={{inv2,inv2},{inv2,mod-inv2}}; int n,a[N],b[N],f[N],g[N],pre; void FWT(int *F,const int c[2][2]) { for(int mx=1;mx<=pre;mx++) { int len=(1ll<<mx); for(int l=0;l<n;l+=len) { for(int i=l;i<l+(len/2);i++) { int x=F[i],y=F[i+(len/2)]; F[i]=(c[0][0]*x%mod+c[0][1]*y%mod)%mod; F[i+(len/2)]=(c[1][0]*x%mod+c[1][1]*y%mod)%mod; } } } } void IFWT(int *F,const int c[2][2]) { for(int mx=pre;mx>=1;mx--) { int len=(1ll<<mx); for(int l=0;l<n;l+=len) { for(int i=l;i<l+(len/2);i++) { int x=F[i],y=F[i+(len/2)]; F[i]=(c[0][0]*x%mod+c[0][1]*y%mod)%mod; F[i+(len/2)]=(c[1][0]*x%mod+c[1][1]*y%mod)%mod; } } } } void RE() { for(int i=0;i<n;i++) f[i]=a[i],g[i]=b[i]; } void PR() { for(int i=0;i<n;i++) cout<<(f[i]%mod+mod)%mod<<' '; cout<<'\n'; } signed main() { cin.tie(0); ios::sync_with_stdio(false); cin>>n; pre=n; n=(1ll<<n); for(int i=0;i<n;i++) cin>>a[i]; for(int i=0;i<n;i++) cin>>b[i]; RE(); FWT(f,Cor); FWT(g,Cor); for(int i=0;i<n;i++) f[i]=f[i]*g[i]%mod; IFWT(f,ICor); PR(); RE(); FWT(f,Cand); FWT(g,Cand); for(int i=0;i<n;i++) f[i]=f[i]*g[i]%mod; IFWT(f,ICand); PR(); RE(); FWT(f,Cxor); FWT(g,Cxor); for(int i=0;i<n;i++) f[i]=f[i]*g[i]%mod; IFWT(f,ICxor); PR(); return 0; }

__EOF__

本文作者F x o r G
本文链接https://www.cnblogs.com/xugangfan/p/17187466.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   FxorG  阅读(40)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2022-03-07 结论 区间加
2022-03-07 结论 ARC136B B - Triple Shift
点击右上角即可分享
微信分享提示