Skip to content

Commit

Permalink
2.6
Browse files Browse the repository at this point in the history
  • Loading branch information
dtcxzyw committed Feb 6, 2019
1 parent 5964d85 commit c5e4b59
Show file tree
Hide file tree
Showing 22 changed files with 790 additions and 23 deletions.
7 changes: 7 additions & 0 deletions Queue.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@
- [ ] BZOJ3684: 大朋友和多叉树
- [ ] BZOJ3456
- [ ] LOJ#6363. 「地底蔷薇」
- [ ] P4705
- [ ] P4389
- [ ] BZOJ3456
- [x] BZOJ3160: 万径人踪灭
- [x] BZOJ4503
- [x] BZOJ4259
# 后缀数组/后缀自动机/序列自动机
- [x] LOJ#111. 后缀排序
- [ ] LOJ#544. 「LibreOJ β Round #7」Array Poisonous Suffix Problem
Expand Down Expand Up @@ -206,6 +212,7 @@
- [ ] P3920
- [ ] P2664
- [ ] BZOJ4568
- [ ] BZOJ3730: 震波
# 虚树
- [x] LOJ#2206. 「HNOI2014」世界树
- [x] LOJ#2219. 「HEOI2014」大工程
Expand Down
2 changes: 2 additions & 0 deletions Review/Bib.tex
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,6 @@
\bibitem{BPSW} Robert Baillie; Samuel S. Wagstaff, Jr. (October 1980).
"Lucas Pseudoprimes" (PDF). Mathematics of Computation. 35 (152):
1391–1417.
\bibitem{LAIA5} David C. Lay , Steven R. Lay , Judi J. McDonald .
Linear Algebra and Its Applications 5th Edition
\end{thebibliography}
17 changes: 17 additions & 0 deletions Review/Graph/Other.tex
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,20 @@ \subsubsection{推广}
Lindström–Gessel–Viennot lemma - Wikipedia
\url{https://en.wikipedia.org/wiki/Lindstr\%C3\%B6m\%E2\%80\%93Gessel\%E2\%80\%93Viennot\_lemma}
}。
\subsection{三元环计数}
给定一个$n$个点$m$条边,无重边无自环的无向图,求无序三元组$(i,j,k)$的个数,其中且两两有边。

算法步骤如下:
\begin{itemize}
\item 对无向图的边进行重定向,度数小的点连向度数大的点,若度数相同则编号小的点连向
编号大的点。
\item 枚举点$u$,将$u$的邻接点标为已访问,再枚举$u$的邻接点$v$,枚举$v$的邻接点$w$
$w$$u$访问,则$(u,v,w)$是一个合法三元组。
\end{itemize}

该算法可以保证不重不漏地计数。若$n,m$同阶,时间复杂度$O(m\sqrt{m})$

正确性与时间复杂度参见KingSann的博客\footnote{
不常用的黑科技——「三元环」
\url{https://www.cnblogs.com/KingSann/p/9590525.html}
}。
48 changes: 48 additions & 0 deletions Review/LinearAlgebra/LinearRecurrence.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
\section{常系数齐次线性递推}
\index{L!Linear Difference Equation}
给定系数$a_0,a_2,\cdots,a_k$,有一个信号${f_n}$,满足$k$阶齐次线性差分方程
$\displaystyle \sum_{i=0}^k{a_if_{n-i}}=0$对所有$n$成立。现在给定信号
${f_n}$中的连续$k$项,求信号的任意一项。

一般$a_0$取1,其余系数取反,那么有$\displaystyle f_n=\sum_{i=1}^k{a_if_{n-i}}$
假设给定了$f_0,f_1,\cdots,f_{k-1}$的值,现在要求出$f_n$的值。

如果$k$足够小,那么很容易构造转移矩阵,记向量$F_i$表示$(f_{i+k-1},f_{i+k-2},\cdots,f_i)$
很容易构造$k*k$的转移矩阵$A$,其中$i$$i+1$转移系数为1,$i$$1$转移系数为$a_i$。即
$A[i+1][i]=1,A[1][i]=a_i$,那么有$F_n=A^nF_0$。使用矩阵快速幂可得到
$O(k^3\lg n)$的算法。

注意原等式左右向量取第$k$项仍然成立,即$(F_n)_{[k]}=(A^nF_0)_{[k]}$。左边就是$f_n$
右边就是以$A^n$的第$k$行为系数的$f_{0,\cdots,k-1}$的线性组合。记这些系数为
$c_{0,\cdots,k-1}$,同样将$f_{0,\cdots,k-1}$表示为$A^nF_0$的形式,有
$A^n=\displaystyle \sum_{i=0}^{k-1}{c_iA^i}$,记右式为矩阵$A$的多项式表达$R(M)$
设存在矩阵多项式$F(M),G(M)$,满足$G(M)$的次数为$k$$F(M)G(M)$的次数为$n$
$M^n=F(M)G(M)+R(M)$。根据Cayley-Hamilton定理,若$G(M)$为矩阵$A$的特征多项式,
其次数恰好为$k$$G(A)=0$。由于$G(M)$的次数为$k$$R(M)\equiv M^n\pmod{G(M)}$
多项式取模可求出$R(M)$。并且由于$G(A)=0$,此时有$A^n=R(A)$。拿到$R(M)$后就可以直接$O(k)$
求值。

接下来讨论如何构造出矩阵$A$的特征多项式$G(M)$。根据定义有
$G(\lambda)=\textrm{det}(\lambda I_k-A)$,多项式高斯消元求行列式十分麻烦。
注意到矩阵$\lambda I_k-A$的特殊性,将第一行每一项的代数余子式求和,去除第一行第$i$
列后都会得到一个下三角矩阵,行列式值为对角线上元素之积,同时代数余子式的$(-1)^{i+1}$
与子矩阵的$-1$项恰好抵消。综上所述,$G(\lambda)=\lambda^k-a_1\lambda^{k-1}-\cdots-a_k$

因此该方法的性能瓶颈在多项式取模上,时间复杂度$O(k\lg k\lg n)$。引入$\lg n$的原因是
我们无法直接构造一个多项式$x^n$然后以$n$的规模做取模,由于该多项式较为简单,可以使用类似
模意义快速幂的方法做模$R(M)$意义下的多项式快速幂。

好写的优化技巧:
\begin{itemize}
\item 预处理出$G(x)$$G_{rev}^{-1}(x)$的点值表达。
\item 需要取模时才实例化取模。
\end{itemize}

参考代码:
\lstinputlisting{Source/Templates/LR.cpp}

上述内容参考了《线性代数及其应用》\cite{LAIA5}4.8节以及shadowice1984的博客
\footnote{
题解 P4723 【【模板】线性递推】
\url{https://www.luogu.org/blog/ShadowassIIXVIIIIV/solution-p4723}
}。
5 changes: 4 additions & 1 deletion Review/Main.tex
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,13 @@
\frontmatter
\maketitle
\chapter{前言}
本人写复习笔记的目的有两个
本人写复习笔记的目的有\sout{两个}五个
\begin{itemize}
\item 系统地复习知识点并挖掘一些有用的性质。
\item 学会使用\LaTeX{}。
\item 预先踩坑并记录,而不是在考场上首次掉坑,整场都在Debug。
\item 以此纪念我短暂的OI生涯,并为将来的ACM做准备。
\item \sout{给学弟留下一些有用的东西。}
\end{itemize}
当前共\pageref{LastPage}页,\input{../latex/charcnt}字。
Expand Down
24 changes: 22 additions & 2 deletions Review/Math/GeneratingFunction.tex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
\section{生成函数}\label{GF}
\index{G!Generating Function}
{\bfseries 生成函数为形式幂级数,不考虑其是否发散或收敛。}
\sout{生成函数为形式幂级数,假装其收敛。}
\subsection{普通型生成函数}
若数列$<f_0,f_1,\cdots>$,若形式幂级数
\begin{displaymath}
Expand Down Expand Up @@ -96,7 +96,7 @@ \subsection{自然数幂求和}
\begin{displaymath}
\sum_{i=0}^n{\binomial{n+1}{i}B_i}=0
\Leftrightarrow
B_n=-\frac{1}{n+1}\sum{i=0}^{n-1}{\binomial{n+1}{i}B_i}
B_n=-\frac{1}{n+1}\sum_{i=0}^{n-1}{\binomial{n+1}{i}B_i}
\end{displaymath}
$O(k^2)$预处理。

Expand All @@ -118,3 +118,23 @@ \subsection{自然数幂求和}
\url{http://blog.miskcoo.com/2015/05/polynomial-inverse}
}
的博客。
\subsection{生成函数处理背包问题}
生成函数法用来计算大规模背包的方案数。
\subsubsection{多重背包}
每个物品的生成函数为$\displaystyle \sum_{k=0}^{c_i}{x^{kw_i}}$,化简为
$\frac{1-x^{(c_i+1)w_i}}{1-x^{w_i}}$。由于乘积指数项的大小不可控,考虑将
乘积变为取对数求和。设答案为$A(x)$,那么有
$\displaystyle \ln A(x)=\sum_{i=1}^n{\ln(1-x^{(c_i+1)w_i})-\ln (1-x^{w_i})}$
使用泰勒展开$\displaystyle \ln(1-x^k)=-\sum_{i=1}^\infty{\frac{x^{ki}}{i}}$得到
$ln A(x)$后多项式exp即为答案。注意$\ln (1-x^k)$要合并同类项,否则一堆$1-x$可以轻易将其
卡掉。合并同类项后根据调和级数可知构造多项式的复杂度为$O(n\lg n)$$n$为背包容量。
\subsubsection{01背包}
每个物品的生成函数为$1+x^{w_i}$,总方案的生成函数为这些函数之积。不过由于指数上界
$\sum{w_i}$不可控,只能转化为多重背包解决。
\subsubsection{完全背包}
背包的容量肯定已知,因此完全背包可转化为多重背包。

该讨论源自AntiLeaf的题目「Antileaf's Round」咱们去烧菜吧\footnote{
AntiLeaf's Round 题解
\url{https://loj.ac/article/304}
}。
1 change: 1 addition & 0 deletions Review/Other/TricksAndIdeas.tex
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@ \subsection{注意事项/常见转化/思想}
\item 节点到根的路径上的点权值+1,统计某个点的权值。\sout{树链剖分+区间修改单点查询。}
使用树上差分直接在该点修改,查询时查询子树权值和,可以$O(n)$预处理$O(1)$查询,比原方法
修改少$\lg^2 n$,查询少$\lg n$
\item 多个卷积后的函数之和可以在点值乘法时就相加,最后只做一次IDFT。
\end{itemize}
\subsection{本节注记}
2013年许昊然的国家集训队论文答辩《浅谈数据结构题的几个非经典解法——<Claymore>命题报告》
Expand Down
3 changes: 2 additions & 1 deletion Review/Path/SMSSP.tex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ \subsection{Dijkstra}
大型考试还是使用Dijkstra算法好了。

注意Dijkstra中不方便的堆内修改可以改为加入新点(标号+松弛距离)的形式,
出堆时与数组中记录的距离值比较,匹配才进行松弛。
出堆时与数组中记录的距离值比较,匹配才进行松弛。当然由于每个节点只会松弛一次,
使用flag记录是否松弛过也可以正确运行,这个方法在扩展Dijkstra中比较好用。
\subsection{Johnson算法}
Johnson算法用于求所有节点对间最短路径。
其主要思想是将原图转换为无负权边的图。
Expand Down
3 changes: 3 additions & 0 deletions Review/Recommendation.tex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ \section{好用的网站/工具}
\item 3Blue1Brown \url{http://www.3blue1brown.com/} 脑洞有趣易懂的数学教程
\item Graphviz \url{http://www.graphviz.org/} 图形可视化工具
\item Project~Euler \url{https://projecteuler.net/} 数学难题集
\item BZOJ离线题库(BZOJCH)[By 阮行止] \\\url{http://ruanx.pw/bzojch/index.html}
\item BZOJ题号查找器(BZOJNO)[By 阮行止]
\url{http://ruanx.pw/bzojch/bzojno.html}
\end{itemize}
\section{优秀资料}
\begin{itemize}
Expand Down
60 changes: 60 additions & 0 deletions Review/String/Convolution.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
\section{卷积法解决字符串匹配问题}
\subsection{回文子序列}
以某个位置为对称轴的回文子序列的个数可以由关于这个位置对称的字符对数计算。
每一对都有选与不选两种选择,除去全不选的情况,记对称字符对数为$k$,方案为$2^k-1$

接下来考虑如何计算出对称字符对数。若字符串按照Manacher算法处理,对于每个对称中心$i$
以它为对称中心的字符对满足$S[i-x]=S[i+x]$,注意到$i-x+i+x=2i$为定值,可以联系到卷积。
枚举字符集的字符,将有该字符的位置标为1,其余标为0,做一遍自卷积,位置$2i$的系数指示了以
$i$为对称中心的当前字符对数。由于同一个位置上会被统计1次,不同位置的对会被统计2次,所以
(系数+1)/2才是实际对数。时间复杂度$O(|\Sigma|n\lg n)$

事实上卷积时不一定用Manacher算法预处理,将偶回文序列的对称轴看做$x.5$,其两倍仍然是
整数,可直接统计$[1,2n]$全部系数。

\subsubsection{例题} BZOJ3160: 万径人踪灭

本题要求的是回文子序列数,去掉是连续一段的回文子串。回文子序列数可以使用FFT卷积或者
序列自动机实现,回文子串数可以用Manacher或者PAM实现。

参考代码(NTT+Manacher):
\lstinputlisting{Source/Source/'FFT NTT'/BZOJ3160.cpp}

由于卷积出的值很小(在$n$的范围内),FFT、NTT均可,注意控制FFT的精度(做完除法操作后
使用固定eps,如果想要省去除法操作,需要将eps乘以FFT规模作为实际eps)。
\subsection{带通配符匹配}
给定母串$S$与带通配符的模板串$P$,求$P$$S$中的出现位置。

首先考虑不带通配符匹配的问题,可以使用KMP解决(带通配符则无法保持nxt的性质),但也有卷积
的方法。考虑如何将其表示为卷积的形式。如果母串$S$在位置$i$处匹配了$P$,那么有
$S[i+k-1]=P[k],1\leq k \leq|P|$。等式两边的下标之和不为定值,但它们的差为定值。那么
可以将$P$取反为$P_{rev}$,有$S[i+k-1]=P_{rev}[|P|-k+1],1\leq k \leq |P|$,两边
下标之和为$i+|P|$,可以进行卷积。同样考虑枚举字符集的字符,将有该字符的位置置为1,其余置0。
将每次卷积的结果累加,若位置$i+|P|$上的系数为$|P|$,则说明母串$S$在位置$i$匹配上了$P$

有通配符的情况类似,每个有通配符的位置强制置1。

这种方法的时间复杂度仍为$O(|\Sigma|n\lg n)$
\subsection{大字符集处理}
对于$|\Sigma|$较大的情况(比如26个字母),26次DFT的时间无法被接受。考虑如何把它们
放在一个式子内计算。考虑不带通配符的情况,将字母表示为数字,对应位相等则数字差为0。用
区间内差的绝对值之和为0表示整段对应区间数字差为0比较麻烦,索性使用平方和。那么有
$V[x]=\displaystyle \sum_{i=1}^{|P|}{(S[x+i-1]-P[i])^2}=0$,将平方展开,$P$
取反得到
\begin{displaymath}
V[x]=\displaystyle \sum_{i=1}^{|P|}{S[x+i-1]^2+P_{rev}[|P|-i+1]^2-2S[x+i-1]P_{rev}[|P|-i+1]}
\end{displaymath}
仅需做一次卷积。考虑带通配符的情况,通配符无法表示为与26个数字都相等的数字,但是可以令其为0,
作为平方和的系数,也可以使整个式子的值为0。将式子拆开后可表示为两个卷积+一个常数的形式。
如果母串也带通配符,则再乘一个系数,表示为三个卷积之和。

参考代码:
\lstinputlisting{Source/Source/'FFT NTT'/BZOJ4503.cpp}

{\bfseries 使用FFT时,若最后不做除法,eps要开大些,比如0.5*p。
可以在做点值乘法时直接求和,仅需一次IDFT。}

上述内容参考了小蒟蒻yyb的博客\footnote{
[复习]多项式和生成函数相关内容
\url{https://www.cnblogs.com/cjyyb/p/10132855.html}
}。
2 changes: 2 additions & 0 deletions Review/String/KMP.tex
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ \subsubsection{匹配}
}
\end{lstlisting}

有时nxt数组会被用来辅助dp转移,构造出dp转移方程后使用矩阵快速幂加速。

ExKMP已被更好理解的Z Algorithm取代,故不再补充该内容。
Z Algorithm参见第~\ref{ZA}节。
30 changes: 19 additions & 11 deletions Review/String/Manacher.tex
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,36 @@ \section{Manacher算法}
\item 答案即为$RL[i]-1$的最大值。
\end{enumerate}

\paragraph{小trick}
\begin{itemize}
\item 在字符串开头再加另一个特殊字符,可以不用越界检查。
\item 令maxr为当前匹配最右边的位置的右边一位,避免+1-1的麻烦。
\end{itemize}

这两个trick源自小蒟蒻yyb的博客\footnote{
【BZOJ3160】万径人踪灭(FFT,Manacher)
\url{https://www.cnblogs.com/cjyyb/p/8435460.html}
}。

代码如下:
\begin{lstlisting}
char buf[size],str[2*size];
int RL[2*size];
int manacher() {
int cnt=0;
str[cnt++]='#';
str[cnt++]='@';
for(int i=0;buf[i];++i) {
str[cnt++]='@';
str[cnt++]=buf[i];
str[cnt++]='@';
}
str[cnt++]='@';
int maxr=0,pos=0,ans=0;
for(int i=0;i<cnt;++i) {
if(i<maxr)
RL[i]=std::min(RL[2*pos-i],maxr-i+1);
else
RL[i]=1;
while(i-RL[i]>=0 && i+RL[i]<cnt &&
str[i-RL[i]]==str[i+RL[i]])
for(int i=1;i<cnt;++i) {
RL[i]=(maxr>i?std::min(RL[2*pos-i],maxr-i):1);
while(str[i-RL[i]]==str[i+RL[i]])
++RL[i];
if(i+RL[i]-1>maxr) {
maxr=i+RL[i]-1;
if(i+RL[i]>maxr) {
maxr=i+RL[i];
pos=i;
}
ans=std::max(ans,RL[i]);
Expand Down
1 change: 1 addition & 0 deletions Review/String/SAM.tex
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ \subsubsection{应用}
预处理出两个字符串的序列自动机后,使用记忆化搜索在序列自动机上跑。
\paragraph{回文子序列个数}
对原串和反串构建序列自动机,求这两个串的公共子序列数。

记记忆化搜索调用为$DFS(x,y)$$x,y$分别为在这两个串上的匹配位置,有
$x\leq n+1-y$,等号成立意味着该回文序列为奇序列。但是奇序列不一定满足
其等号成立,如果记忆化搜索搜索到一个偶回文序列,删掉该回文序列中心的一个字符,
Expand Down
1 change: 1 addition & 0 deletions Review/String/String.tex
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ \chapter{字符串}
\input{String/SAM}
\input{String/Parser}
\input{String/ZAlgorithm}
\input{String/Convolution}
Loading

0 comments on commit c5e4b59

Please sign in to comment.