FWT 笔记 II
为了实现 FWT,我们需要实现所谓带权前缀和。
具体来说,给定序列 \(\{A\}_{i=0}^{c^n-1}\)(这里我们推广了一般的二进制情况),以及一个函数 \(w(i,j)\quad i \in [0,d^n),j\in [0,c^n)\),我们需要求出:
我们记 \(x^{(y)}_z\) 表示 \(x\) 在 \(y\) 进制下的第 \(z+1\) 位。
当上下文语意明确时,我们省略上标。
一般为了方便求解,\(w\) 有性质:
好的,怎么求解这个一般的 FWT 呢?
我们这里可以建出 \(c\) 进制的字典树,每一次我们维护子树内变换后的结果。
那么用转移的直接合并就可以了。
对于 \(c=d\) 的情况,可以原地非递归实现。
复杂度若 \(c=d\) 是 \(\Theta(nc^{n+1})\) 的,否则是 \(\Theta(c\sum_{0\le i<n}c^id^{n-i})=\Theta\left(cd^n\dfrac{1-(c/d)^n}{1-c/d}\right)\)。
我们一般认为 \(c,d\) 是常数,于是复杂度 \(\Theta(\max\{c,d\}^n)\)(如果 \(c\ne d\)。)
接下来考虑做卷积。
卷积
卷积就是给定两个序列 \(\{A_i\}_{i=0}^{c^n-1},\{B_i\}^{c^n-1}_{i=0}\),以及 \(c\) 进制位运算 \(\textup{op}\),求:
我们一般需要三个 \(\textup{FWT}\),使得 \(C=\textup{FWT}_3(\textup{FWT}_1(A)\cdot \textup{FWT}_2(B))\),这里 \(\cdot\) 表示对应相乘。
我们记三个 \(\textup{FWT}\) 的 \(w\) 分别为 \(w_1,w_2,w_3\),再记 \(C_k=\sum_{i,j}w(k,i,j)A_iB_j\)。
那就是
也就是说
我们知道这些 \(w\) 每一位都是独立的,所以:
第二步变换是因为 \(\mathscr{l}\) 恰好遍历了所有可能。
同时注意 \(\mathscr{l}\) 其实不一定是 \(c\) 进制的,这里我们设其为 \(d\) 进制。
于是
这是什么?
我们记 \(\mathbf{W}^{(k)}_{i,j}=w(k,i,j),\lambda_{k,\mathscr{l}}=w_3(k,\mathscr{l}),\mathbf{V}^{(\mathscr{l})}_{i,j}=w_1(\mathscr{l},i)w_2(\mathscr{l},j)\quad 0\le i,j,k<c\quad 0\le \mathscr{l}<d\)。
那么就可以简单地写成:
也就是说,给定 \(c\) 个 \(c\times c\) 矩阵。我们需要构造 \(d\) 个 \(c\times c\) 特殊的矩阵,使得给定的每个矩阵都可以表示为构造矩阵的线性组合。
你构造的矩阵应该可以被写成列向量和行向量的乘积,也就是说这些矩阵每行每列成比例(秩为 \(1\)),当然构造的矩阵越少越好。
这被称作张量分解。
只要知道张量分解,我们便能知道这三个 \(\textup{FWT}\) 变换分别是什么从而实现。
假设卷积是 \(c\) 进制的,你使用了 \(d\) 个矩阵,若 \(c\ne d\),复杂度为 \(\mathcal{O}(\max\{c,d\}^n)\),若 \(c=d\),复杂度为 \(\mathcal{O}(nc^n)\)。
但是,最小张量分解是 NP-Hard 的,这使得 \(\textup{FWT}\) 很大程度上依赖于直觉(或者是超级暴力。)
例题
ARC132F Takahashi The Strongest
首先可以转换为从未获胜的概率。
考虑一下,这其实就是卷积,我们用矩阵写成这样:
怎么构造张量分解?
观察数据范围,我们可以用 \(4\) 个矩阵。
那分别是
直接 FWT。
#include<bits/stdc++.h>
using namespace std;
using ull=unsigned long long;
int n;
int pwc[13],pwd[13];
void init(){
pwc[0]=pwd[0]=1;
for(int i=1;i<=n;++i){
pwc[i]=pwc[i-1]*3;
pwd[i]=pwd[i-1]*4;
}
}
int w1[4][3]={
{1,1,1},
{0,1,0},
{0,0,1},
{1,0,0}
};
int w2[3][4]={
{1,-1,0,0},
{1,0,-1,0},
{1,0,0,-1}
};
const int V=2e7;
ull tmp[V+5];
void FWT(int now,ull* A,ull *B){
if(now==n){
B[0]=A[0];
return;
}
for(int i=0;i<3;++i) FWT(now+1,A+i*pwc[n-now-1],B+i*pwd[n-now-1]);
for(int i=0;i<pwd[n-now];++i) tmp[i]=0;
for(int i=0;i<4;++i){
for(int j=0;j<3;++j){
for(int k=0;k<pwd[n-now-1];++k){
tmp[i*pwd[n-now-1]+k]+=w1[i][j]*B[j*pwd[n-now-1]+k];
}
}
}
for(int i=0;i<pwd[n-now];++i) B[i]=tmp[i];
}
void IFWT(int now,ull *A,ull *B){
if(now==n){
B[0]=A[0];
return;
}
for(int i=0;i<4;++i) IFWT(now+1,A+i*pwd[n-now-1],B+i*pwd[n-now-1]);
for(int i=0;i<pwc[n-now];++i) tmp[i]=0;
for(int i=0;i<3;++i){
for(int j=0;j<4;++j){
for(int k=0;k<pwc[n-now-1];++k){
tmp[i*pwc[n-now-1]+k]+=w2[i][j]*B[j*pwd[n-now-1]+k];
}
}
}
for(int i=0;i<pwc[n-now];++i) B[i]=tmp[i];
}
ull AA[V+5],BB[V+5],CC[V+5],DD[V+5];
int m,k;
int main(){
cin>>n>>m>>k;
for(int i=1;i<=m;++i){
string str;
cin>>str;
int res=0;
for(int j=0;j<n;++j){
res=res*3+(str[j]=='P'?0:(str[j]=='R'?1:2));
}
++AA[res];
}
for(int i=1;i<=k;++i){
string str;
cin>>str;
int res=0;
for(int j=0;j<n;++j){
res=res*3+(str[j]=='P'?0:(str[j]=='R'?1:2));
}
++BB[res];
}
init();
FWT(0,AA,CC);
FWT(0,BB,DD);
for(int i=0;i<pwd[n];++i){
CC[i]*=DD[i];
}
IFWT(0,CC,DD);
for(int i=0;i<pwc[n];++i){
cout<<(ull)m*k-DD[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号