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


首先我们以二进制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. ActiveMQ 消息确认

    一.事务性会话:当一个事务被提交的时候,确认自动发生 ConnectionFactory connectionFactory=new ActiveMQConnectionFactory("t ...

  2. Linux删除Tomcat中产生的所有log文件

    #!/bin/bash #!/bin/bash #exact all log files #计算log文件个数log_number=`ls *.log |grep log -c` #当log文件数大于 ...

  3. Thrift RPC Golang、C++ Example

    Thrift RPC Example 运行 请直接使用即可,无需拉取任何依赖包. cd $GOPATH/src git clone https://github.com/hunterhug/thrif ...

  4. 洛谷 P1540 机器翻译

    链接:https://www.luogu.org/problemnew/show/p1540 题目: 题目背景 小晨的电脑上安装了一个机器翻译软件,他经常用这个软件来翻译英语文章. 题目描述 这个翻译 ...

  5. 初学java1 数据类型

    java数据类型 分为8种 整型 byte 8位 short 16位 int 32位 long 64位 字符型 char 必需为单引号'' 且只能有一个字符 浮点型 float double 布尔类型 ...

  6. JAVA文件IO总结

    文件流的操作有两种:字节流.字符流 字节流:FileInputStream,FileInputStream 字符流:InputStreamReader,OutputStreamReader 这两种流对 ...

  7. JS数组抽奖程序教学实例

    数组Javascript中非常重要的知识点,为了在课堂上提高学生兴趣,教学举例的选择就比较重要了. 为了提高学生兴趣,特设计一个可输入,可控制结束的,利用JS数组实现的抽奖教学实例.代码如下:

  8. ntp时间同步简介

    网络时间协议(Network Time Protocol) 安装 # sudo apt-get install ntp 官网下载:http://www.ntp.org/downloads.html 本 ...

  9. 阿里P7分享如何面对枯燥的源码

    一个软件开发人员,工作到了一定的年限(一般是3.4年左右),如果他还没学会阅读源码,那么他就会遇到瓶颈.因为到了这个时候的开发,他应该不仅仅只会做那些 CURD 的业务逻辑,而应该会根据公司的实际情况 ...

  10. 作为一个java高级工程师的进阶之路

    本文可能可能更偏向于是内心的独白篇和面试技巧总结 一.独白 之前也面试别人,现在轮到自己找工作,怎么说呢,每个面试官的看法不一样,面试的方式就不一样,比如我面试别人我喜欢问项目中他用到了那些,然后针对 ...