====== 多项式应用 ======
===== 习题一 =====
[[https://www.luogu.com.cn/problem/P3702|洛谷p3702]]
==== 题意 ====
询问有多少序列满足下列条件:
- 序列长度等于 $n$
- 序列由不超过 $m$ 的正整数构成且至少含有一个素数
- 序列所有数之和是 $p$ 的倍数
==== 题解 ====
考虑容斥,于是答案为由不超过 $m$ 的正整数构成的序列数减去不超过 $m$ 的合数构成的序列数。
不超过 $m$ 且模 $p$ 为 $i$ 的正整数个数,而利用线性筛可以计算出不超过 $m$ 且模 $p$ 为 $i$ 的合数个数。
设 $\text{dp}(i,j)$ 表示长度为 $i$ 且和模 $p$ 为 $j$ 的序列数,发现可以利用倍增 $+$ $\text{NTT}$ 转移。时间复杂度 $O(m+p\log n\log p)$。
本题 $p\le 100$ 且模数为合数,所以考虑直接暴力转移,时间复杂度 $O(m+p^2\log n)$。
const int MAXM=2e7+5,MAXP=105,Mod=20170408;
int prime[MAXM],p_cnt,p;
bool vis[MAXM];
void get_prime(int n){
vis[1]=true;
_rep(i,2,n){
if(!vis[i])prime[p_cnt++]=i;
for(int j=0;i*prime[j]<=n&&j=0;i--){
if(n&(1<
===== 习题二(字符串匹配) =====
[[https://www.luogu.com.cn/problem/P4173|洛谷p4173]]
==== 题意 ====
给定只包含小写字母和通配符的字符串 $A,B$。其中 $A$ 为模板串,$|A|=m,|B|=n$。询问所有匹配位置。
==== 题解 ====
构成字符集到数值的映射
$$
f(x)=
\begin{cases}
x-a &x= a\sim z\\
0 &x=*
\end{cases}
$$
令 $c_i=\sum_{j=0}^{m-1}\left(f(a_j)-f(b_{i-m+1+j})\right)^2f(a_j)f(b_{i-m+1+j})$。
于是 $B$ 中第 $i$ 个字符结尾的长度为 $m$ 的字符串与 $A$ 匹配当且仅当 $c_i=0$。令 $t_i=a_{m-1-i}$,有
$$\sum_{x+y=i}\left(f(t_x)-f(b_y)\right)^2f(t_x)f(b_y)=\sum_{x+y=i}f(t_x)^3f(b_y)+\sum_{x+y=i}f(t_x)f(b_y)^3-2\sum_{x+y=i}f(t_x)^2f(b_y)^2$$
于是直接 $\text{FFT}$ 即可,时间复杂度 $O(n\log n)$。
const int MAXN=3e5+5;
const double pi=acos(-1.0);
struct complex{
double x,y;
complex(double x=0.0,double y=0.0):x(x),y(y){}
complex operator + (const complex &b){
return complex(x+b.x,y+b.y);
}
complex operator - (const complex &b){
return complex(x-b.x,y-b.y);
}
complex operator * (const complex &b){
return complex(x*b.x-y*b.y,x*b.y+y*b.x);
}
complex operator * (const int &b){
return complex(x*b,y*b);
}
}w[32][2];
int rev[MAXN<<2];
int build(int k){
int n,pos=0;
while((1<>1]>>1)|((i&1)<<(pos-1));
for(int i=1,t=0;i
===== 习题三(差分与前缀和) =====
[[https://www.luogu.com.cn/problem/P5488|洛谷p5488]]
==== 题意 ====
给定一个长度为 $n$ 的序列 $a$,求 $a$ 的 $k$ 阶差分或前缀和。
==== 题解 ====
设序列的生成函数为 $F(x)=\sum_{i=0}^{n-1} a_ix^i$,则 $k$ 阶差分等价于
$$
F(x)(1-x)^k=\sum_{i=0}^{n-1}a_ix^i\sum_{i=0}^{n-1}(-1)^i{i\choose k}=\sum_{i=0}^{n-1}a_ix^i\sum_{i=0}^{n-1}(-1)^i\frac {k^{\underline i}}{i!}
$$
而 $k$ 阶前缀和等价于
$$
F(x)(1+x+x^2+x^3+\cdots)^k=F(x)(1-x)^{-k}=\sum_{i=0}^{n-1}a_ix^i\sum_{i=0}^{n-1}(-1)^i\frac {(-k)^{\underline i}}{i!}=\sum_{i=0}^{n-1}a_ix^i\sum_{i=0}^{n-1}\frac {k^{\overline i}}{i!}
$$
直接 $O(n)$ 递推出系数然后 $\text{NTT}$ 卷积即可。时间复杂度 $O(n\log n)$。
const int MAXN=1e5+5,Mod=1004535809;
int quick_pow(int a,int b){
int t=1;
while(b){
if(b&1)
t=1LL*t*a%Mod;
a=1LL*a*a%Mod;
b>>=1;
}
return t%Mod;
}
namespace Poly{
const int G=3;
int rev[MAXN<<2],Pool[MAXN<<3],*Wn[30];
void init(){
int lg2=0,*pos=Pool,n,w;
while((1<>=1;
}
}
int build(int k){
int n,pos=0;
while((1<>1]>>1)|((i&1)<<(pos-1));
return n;
}
void NTT(int *f,int n,bool type){
_for(i,0,n)if(i
===== 习题四(矩阵卷积) =====
==== 题意 ====
考虑快速计算序列 $\{C\}$,其中 $A_i,B_i$ 均为 $w\times w$ 矩阵
$$
C_i=\sum_{j=0}^i A_jB_{i-j}(0\le i\le n)
$$
==== 题解 ====
不难发现 $c_{ij}=\sum_{k=1}^w a_{ik}b_{kj}$,于是 $c_{ij}$ 可以拆分成 $w$ 个卷积。
考虑 $O\left(w^2n\log n\right)$ 预处理出 $\{a_{ij}\}$ 和 $\{b_{ij}\}$ 的 $\text{DFT}$ 结果。然后通过 $c_{ij}=\sum_{k=1}^w a_{ik}b_{kj}$ 可以 $O\left(w^3n\right)$ 计算每个 $\{c_{ij}\}$ 的 $\text{DFT}$ 结果。
最后对每个 $\{c_{ij}\}$ 做 $\text{IDFT}$,总时间复杂度 $O\left(w^2n\log n+w^3n\right)$。