之前做了那么多生成函数和多项式卷积的题目,结果今天才理解了优化卷积算法的实质。


首先我们以二进制FWT or作为最简单的例子入手。

我们发现正的FWT or变换就是求$\hat{a}_j=\sum_{i\in j}a_i$,即子集和,那这个是怎么来的呢?

我们假设$a$到$\hat{a}$的转移矩阵为$X$,则

$$(\sum_{j}X_{i,j}a_j)*(\sum_{j}X_{i,j}b_j)=\sum_jX_{i,j}(\sum_{s|t=j}a_sb_t)$$

所以考虑$a_sb_t$的贡献。

$$X_{i,s}*X_{i,t}=X_{i,s|t}$$

所以对于$X$的每一行都有$X_s*X_t=X_{s|t}$

而且由于最后还要进行逆变换,也就是乘上$X^{-1}$,我们知道矩阵可以求逆当且仅当$X$的行列式不为0,所以$X$的任意两行都不相同。

根据这个,我们先假设$X$中只有0和1(因为这样是最简单的),然后$X_{s|t}=1$与$X_s=X_t=1$等价,所以就可以推出来了。

先看$n=8$的情形。

$$X=\begin{pmatrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 \\1 & 1 & 0 & 0 & 0 & 0 & 0 & 0 \\1 & 0 & 1 & 0 & 0 & 0 & 0 & 0 \\1 & 1 & 1 & 1 & 0 & 0 & 0 & 0 \\1 & 0 & 0 & 0 & 1 & 0 & 0 & 0 \\1 & 1 & 0 & 0 & 1 & 1 & 0 & 0 \\1 & 0 & 1 & 0 & 1 & 0 & 1 & 0 \\1 & 1 & 1 & 1 & 1 & 1 & 1 & 1\end{pmatrix}$$

打表找规律可得

$$X_{i,j}=\prod_{k=0}^{n-1}C_{i[2^k],j[2^k]}$$

其中$i[2^k]$表示$i$在二进制下的第$k$位。

$$C=\begin{pmatrix}1 & 0 \\ 1 & 1\end{pmatrix}$$

然后我们就知道如何进行分治计算这个向量乘矩阵了。(对,就是那个三重循环)


我们也可以把FFT的矩阵也这样写出来。

$$A=\begin{pmatrix}\omega_n^0 & \omega_n^0 & \ldots & \omega_n^0 & \omega_n^0 \\\omega_n^0 & \omega_n^1 & \ldots & \omega_n^{n-2} & \omega_n^{n-1} \\\vdots & \vdots & \ddots & \ddots & \ddots \\\omega_n^0 & \omega_n^{n-1} & \ldots & \omega_n^{(n-2)(n-1)} & \omega_n^{(n-1)(n-1)}\end{pmatrix}$$

即$A_{i,j}=\omega_n^{ij}$,所以

$$A^{-1}=\frac{1}{n}\begin{pmatrix}\omega_n^{-0} & \omega_n^{-0} & \ldots & \omega_n^{-0} & \omega_n^{-0} \\\omega_n^{-0} & \omega_n^{-1} & \ldots & \omega_n^{-(n-2)} & \omega_n^{-(n-1)} \\\vdots & \vdots & \ddots & \ddots & \ddots \\\omega_n^{-0} & \omega_n^{-(n-1)} & \ldots & \omega_n^{-(n-2)(n-1)} & \omega_n^{-(n-1)(n-1)}\end{pmatrix}$$

即$A^{-1}_{i,j}=\frac{\omega_n^{-ij}}{m}$


UOJ272 【清华集训2016】石家庄的工人阶级队伍比较坚强

我们设$B_{i,j}$表示$f_{i-1}$到$f_i$的转移矩阵。

定义$a\oplus b$表示三进制不进位加法,$a\ominus b$表示三进制不退位减法。易得这两个运算互为逆运算。

则$\forall k,B_{i\oplus k,j\oplus k}=B_{i,j}$,由数学归纳法得$\forall k,B_{i\oplus k,j\oplus k}^n=B_{i,j}^n$即$B_{i,j}^n=B_{0,j\ominus i}^n$

$$f_{n,i}=\sum_{j}f_{0,j}*B_{j,i}^n=\sum_{j}f_{0,j}*B_{0,i\ominus j}^n=\sum_{x\oplus y=i}f_x*B_{0,y}^n$$

所以我们只需要求出$B$矩阵的第一行并与$f_0$做三进制下的异或卷积就可以了。

我们先考虑二进制下的。

$$C=\begin{pmatrix}1 & 1 \\1 & -1\end{pmatrix}$$

($C$矩阵的意义见上)

所以感性理解一下(或者可以自己推一推),三进制的异或卷积的矩阵就是:

$$C=\begin{pmatrix}1 & 1 & 1 \\1 & \omega & \omega^2 \\1 & \omega^2 & \omega\end{pmatrix}$$

$$C^{-1}=\frac{1}{3}\begin{pmatrix}1 & 1 & 1 \\1 & \omega^2 & \omega \\1 & \omega & \omega^2\end{pmatrix}$$

其中$\omega=\frac{-1+\sqrt{3}i}{2}$

但是$\sqrt{3}$运算非常麻烦,还会有精度问题,所以我们取$1,\omega$作为基底而不是$1,i$,即把复数表示成$a+b\omega$的形式。

乘法与$a+bi$的乘法不一样,需要推一推。

$$(a+b\omega)(c+d\omega)=ac+(bc+ad)\omega+bd(-\omega-1)=(ac-bd)+(bc+ad-bd)\omega$$

然后就应该是做完了。

 #include<cstdio>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = ;
int n, m, t, p, po[], cntx[N], cnty[N];
inline void exgcd(int a, int b, int &x, int &y){
if(!b){x = ; y = ; return;}
exgcd(b, a % b, y, x); y -= (LL) a / b * x;
}
struct complex {
int x, y;
inline complex(int x = , int y = ): x(x), y(y){}
inline complex operator + (const complex &o) const {return complex((x + o.x) % p, (y + o.y) % p);}
inline complex operator - (const complex &o) const {return complex((x - o.x + p) % p, (y - o.y + p) % p);}
inline complex operator * (const complex &o) const {
return complex(((LL) x * o.x % p - (LL) y * o.y % p + p) % p, ((LL) y * o.x % p + (LL) x * o.y % p - (LL) y * o.y % p + p) % p);
}
} A[N], B[N];
inline complex kasumi(complex a, int b){
complex res = complex(, );
while(b){
if(b & ) res = res * a;
a = a * a;
b >>= ;
}
return res;
}
inline complex calc1(const complex &a){return complex((p - a.y) % p, (a.x - a.y + p) % p);}
inline complex calc2(const complex &a){return complex((a.y - a.x + p) % p, (p - a.x) % p);}
inline void dft(complex *A){
for(Rint mid = ;mid < n;mid *= )
for(Rint j = ;j < n;j += mid * )
for(Rint k = ;k < mid;k ++){
complex x = A[j + k], y = A[j + k + mid], z = A[j + k + mid * ];
A[j + k] = x + y + z;
A[j + k + mid] = x + calc1(y) + calc2(z);
A[j + k + mid * ] = x + calc1(z) + calc2(y);
}
}
inline void idft(complex *A){
for(Rint mid = ;mid < n;mid *= )
for(Rint j = ;j < n;j += mid * )
for(Rint k = ;k < mid;k ++){
complex x = A[j + k], y = A[j + k + mid], z = A[j + k + mid * ];
A[j + k] = x + y + z;
A[j + k + mid] = x + calc1(z) + calc2(y);
A[j + k + mid * ] = x + calc1(y) + calc2(z);
}
}
int trans[][];
int main(){
scanf("%d%d%d", &m, &t, &p);
po[] = ;
for(Rint i = ;i <= m;i ++) po[i] = (LL) po[i - ] * ;
n = po[m];
for(Rint i = ;i <= m;i ++) po[i] %= p;
if(p == ){
for(Rint i = ;i < n;i ++) puts("");
return ;
}
for(Rint i = ;i < n;i ++) scanf("%d", &A[i].x);
for(Rint i = ;i <= m;i ++)
for(Rint j = ;i + j <= m;j ++) scanf("%d", trans[i] + j);
for(Rint i = ;i < n;i ++){
cntx[i] = cntx[i / ] + (i % == );
cnty[i] = cnty[i / ] + (i % == );
B[i].x = trans[cntx[i]][cnty[i]];
//printf("%d ", B[i].x);
}
//putchar('\n');
dft(A); dft(B);
//for(Rint i = 0;i < n;i ++) printf("(%d, %d)\n", A[i].x, A[i].y);
//for(Rint i = 0;i < n;i ++) printf("(%d, %d)\n", B[i].x, B[i].y);
for(Rint i = ;i < n;i ++) A[i] = A[i] * kasumi(B[i], t);
idft(A);
int inv, tmp;
exgcd(n, p, inv, tmp);
inv = (inv + p) % p;
for(Rint i = ;i < n;i ++)
printf("%d\n", (LL) A[i].x * inv % p);
}

UOJ272

卷积理论 & 高维FWT学习笔记的更多相关文章

  1. 一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记

    一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记 曾经某个下午我以为我会了FWT,结果现在一丁点也想不起来了--看来"学"完新东西不经常做题不写博客,就白学了 = = 我没啥智 ...

  2. FWT学习笔记

    FWT学习笔记 引入 一般的多项式乘法是这样子的: \(c_i=\sum_{i,j}a_j*b_k*[j+k==i]\) 但是如果我们将这个乘法式子里面的+号变换一下变成其他的运算符号呢? \(c_i ...

  3. FMT/FWT学习笔记

    目录 FMT/FWT学习笔记 FMT 快速莫比乌斯变换 OR卷积 AND卷积 快速沃尔什变换(FWT/XOR卷积) FMT/FWT学习笔记 FMT/FWT是算法竞赛中求or/and/xor卷积的算法, ...

  4. $\text {FWT}$学习笔记

    \(\text {FWT}\) 学习笔记 正常项的\(\text {FWT}\) 在\(\text {OI}\)中,我们经常会碰到这种问题: 给出一个长度为\(n\)的序列\(a_{1,2,...,n ...

  5. FWT 学习笔记

    FWT学习笔记 好久以前写的,先粘上来 定义数组 \(n=2^k\) \(A=[a_0,a_1,a_2,a_3,...,a_{n-1}]\) 令\(A_0=[a_0,a_1,a_2,...,a_{\f ...

  6. 快速沃尔什变换 (FWT)学习笔记

    证明均来自xht37 的洛谷博客 作用 在 \(OI\) 中,\(FWT\) 是用于解决对下标进行位运算卷积问题的方法. \(c_{i}=\sum_{i=j \oplus k} a_{j} b_{k} ...

  7. 快速沃尔什变换 FWT 学习笔记【多项式】

    〇.前言 之前看到异或就担心是 FWT,然后才开始想别的. 这次学了 FWT 以后,以后判断应该就很快了吧? 参考资料 FWT 详解 知识点 by neither_nor 集训队论文 2015 集合幂 ...

  8. 快速沃尔什变换(FWT)学习笔记 + 洛谷P4717 [模板]

    FWT求解的是一类问题:\( a[i] = \sum\limits_{j\bigoplus k=i}^{} b[j]*c[k] \) 其中,\( \bigoplus \) 可以是 or,and,xor ...

  9. FWT快速沃尔什变换学习笔记

    FWT快速沃尔什变换学习笔记 1.FWT用来干啥啊 回忆一下多项式的卷积\(C_k=\sum_{i+j=k}A_i*B_j\) 我们可以用\(FFT\)来做. 甚至在一些特殊情况下,我们\(C_k=\ ...

随机推荐

  1. asp.net core-10.Http请求的处理过程

    左边是一个普通的网页请求过程,右边是.net core请求过程 这是大致请求流程图:

  2. 工具——eclipse debug小技巧

    1.开启调试: 在代码编辑处右键单击,在弹出菜单中点击Debug As开始调试 2.几个快捷键: F5:跟入Step into, 一般会跟踪进入到调用函数的函数体,Step Over则不会跟踪进入,直 ...

  3. 关于财务YT知识点

    1 YT 将今年剩余的未花完的money做YT,生成一个YT号,用在下一年使用的机制. 2 生成YT的方式 2.1 PR生成YT 2.2 PO生成YT 2.3 TR生成YT 2.4 预算直接生成YT ...

  4. vs2019 扩展工具

    这里只是做个记录,没啥技术含量 本人代码上有些强迫症,所以我的本地代码一定不可以丢,之前用vs2013开始,就安装了localhistory这个插件,十分方便,觉得不用了,清了即可,也不占地方. 但是 ...

  5. (二)ActiveMQ之点对点消息实现

    一.点对点消息实现概念 在点对点或队列模型下,一个生产者向一个特定的队列发布消息,一个消费者从该队列中读取消息.这里,生产者知道消费者的队列,并直接将消息发送到消费者的队列.这种模式被概括为:只有一个 ...

  6. pickle 和 base64 模块的使用

    pickle pickle模块是python的标准模块,提供了对于python数据的序列化操作,可以将数据转换为bytes类型,其序列化速度比json模块要高. pickle.dumps() 将pyt ...

  7. VBA比较运算符

    VBA支持的比较运算符如下所示. 假设变量A=10,变量B=20,则 - 运算符 描述 示例 = 检查两个操作数的值是否相等.如果是,那么条件是真. (A = B)结果为:False <> ...

  8. nodejs实现邮件发送

    需要安装的node模块 nodemailer 新建项目目录 mail-test 进入这个项目里使用终端初始化package.json(npm init) 安装express和nodemailer并保存 ...

  9. 串口工具kermit(ubuntu)

    安装 # sudo apt-get install ckermit 配置 kermit启动时,会首先查找~/.kermrc,然后再遍历/etc/kermit/kermrc # vi /etc/kerm ...

  10. Java基础加强-类加载器

    /*类加载器*/ 把.class文件从硬盘上加载出来,将类的字节码(二进制)加载到内存中 /*类加载器及其委托机制*/ Java虚拟机中可以安装多个类加载器(可以自己编写),系统默认三个主要类加载器, ...