前置知识:多项式全家桶
简介
定义(组合对象):满足某一性质的树、图、串等可数的对象。
定义(组合类):同一性质的组合对象组成的集合,通常用花体字母如\(\mathcal{A}\)表示。
定义(普通生成函数 OGF):\(A(x)=\sum\limits_{a\in\mathcal{A}}x^{|a|}=\sum\limits_{n\ge0}a_nx^n\)
\(x^n\)为下标=占位符(保存组合类性质,是生成函数构造的核心),组合意义上叫(组合类的)大小。\(a_n\)表示\([x^n]A(x)\),即大小为\(n\)的组合对象的总数目,通俗来说就是你题目想要求的东西,也可以说生成函数是对于序列\(a\)而言的。可以看出第一个等号后是组合意义(也是更直观于题目表现),第二个等号后是多项式,即数学意义。
OGF
鸽着(周末会补)
EGF
也鸽着(周末会补)
PGF
设\(P(表达式)\)为表达式为真的概率。\(X\)为离散型随机变量也就是事件。
-
\(F(x)=\sum\limits_{i\ge0}P(X=i)x^i\)
-
\(F(1)=1\)
- 因为所有事件的概率和为\(1\)。
-
\(E(x)=F'(1)\)
- \(E\)里面的自变量代表事件(也就是加权)
-
\(E(x^{\underline{k}})=F^{(k)}(1)\)
- 系数为下降幂可以通过多次求导得到。
-
\(E(x^2)=E(x(x-1)+x)=F''(1)+F'(1)\)
-
\(Var(x)=E(x^2)-E^2(x)=F''(1)+F'(1)-F(1)^2\)
-
\(Var(x)=E((x-E(x))^2)=E(x^2-2E(x)x+E^2(x))=E(x^2)-E^2(x)\)
-
\(var\)是方差
-
[CTSC2006]歌唱王国
-
题意
给长为\(m\)的\(S\),每次等概率在\([1,c]\)中随机一个数加到\(T\)里(\(T\)初始为空),直到\(T\)中有子串\(S\)停止,输出期望\(|T|\)的后四位。 -
思路
这种题就是套路吧,不懂套路感觉没法做的。
设\(F(x)\)为恰好\(n\)次结束的PGF,\(G(x)\)为\(n\)次时还没结束的PGF。
需要\(G\)是因为\(F\)不好求,而有了\(G\)就可以通过\(F,G\)的关系,列两个等式(方程)来解\(F\)。
目标是\(E(x)=F'(x)\) -
方程1:\(F+G=xG+1\)
- 由\(g\)递推式得,\(g_n=g_{n+1}+f_{n+1}\)
系数转函数:注意左边\(*x\)常数项没有意义,但右边常数项为\(1\)要加上。
- 由\(g\)递推式得,\(g_n=g_{n+1}+f_{n+1}\)
-
方程2:\(G*(\frac{1}{c})^m=\sum\limits_{k=1}^ma_k*F*(\frac{1}{c})^{m-k}\)
- 左边为\(G\)后面直接补了\(T\)但可能在补\(T\)中途就结束了,此时一定会出现前缀=后缀的情况,\(a[i]\)表示\(T\)长度为\(i\)的前后缀是否匹配,因此枚举重叠(相等)长度\(k\)。
-
然后解方程的套路通常是赋值\(x=1\)。
因为要求\(F'(x)\),方程1求导,且赋值\(x=1\)得到:\(F'(1)=G(1)\)。
方程\(2\)同样赋\(1\)得:\(G(1)=\sum\limits_{i=1}^ma_i*c^i\)
因此跑KMP+求解\(G(1)\)即可。 -
code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int mod=1e4;
int pw[N];
int fail[N],s[N];
int main() {
int n,t;scanf("%d%d",&n,&t);
while(t--) {
int m;scanf("%d",&m);
for(int i=1;i<=m;i++) {scanf("%d",&s[i]);}
// fail[1]=0;
for(int i=2,j=0;i<=m;i++) {
while(j&&s[i]!=s[j+1]) {j=fail[j];}
if(s[i]==s[j+1])j++;
fail[i]=j;
// printf("fail[%d]=%d\n",i,j);
}
pw[0]=1;for(int i=1;i<=m;i++) pw[i]=pw[i-1]*n%mod;
int ans=0;
for(int i=m;i;i=fail[i]) {
ans=(ans+pw[i])%mod;
}
printf("%04d\n",ans);
}
return 0;
}
[SDOI2017]硬币游戏
- 题意
跟上一题不同点在于,有\(n\)个长为\(m\)的串\(T_i\),只要任意一个串出现即可停止。 - 思路
同样,设\(F_i\)为在以\(T_i\)结尾结束的pgf,\(G\)为还未结束的pgf。
易得方程1: \(\sum\limits_{i=1}^nF_i=1\)
同上一道题,方程2: \(\sum\limits_{i=1}^nF_i+G=xG+1\)
方程三的构造也和上一道题一样的,但要知道是哪两个字符串的border。
因此记:\(a[i][j][k]\):为\(T_i\)前缀是否和\(T_j\)前缀长\(k\)处匹配。
方程3:\(G*2^{-m}x^m=\sum\limits_{j=1}^n\sum\limits_{k=1}^ma_{i,j,k}F_j(x)2^{k-m}x^{m-k}\)
代入\(x=1\)得:\(G(1)=\sum\limits_{j=1}^n\sum\limits_{k=1}^ma_{i,j,k}F_j(1)2^k\)
方程2其实没什么用,联立方程1和3,\(n+1\)个未知数,\(n+1\)个方程,让Gauss告诉你答案。 - code:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const ll mod=1e9+7;
const int N=305;
const db eps=1e-8;
db pw_2[N],G[N][N];
ll H[N][N],pw2[N];
char s[N];
ll Hash(int i,int l,int r) {return ((H[i][r]-H[i][l-1]*pw2[r-l+1])%mod+mod)%mod;}
void Print(int n) {
printf("matrix:~~~\n");
for(int i=1;i<=n;i++) {
for(int j=1;j<=n+1;j++) {
printf("%.6lf ",G[i][j]);
}
puts("");
}
printf("end matrix:~~~\n");
}
void Gauss(int n) {
// Print(n);
for(int i=1;i<=n;i++) {
int r=i;
for(int j=i+1;j<=n;j++)if(fabs(G[j][i])>fabs(G[r][i])){r=j;}
if(r!=i)swap(G[i],G[r]);
assert(fabs(G[r][i])>eps);
for(int j=i+1;j<=n;j++) {
db d=G[j][i]/G[i][i];
for(int k=1;k<=n+1;k++) {G[j][k]-=G[i][k]*d;}
}
}
for(int i=n;i>=1;i--) {
G[i][n+1]/=G[i][i];
for(int j=1;j<i;j++) {G[j][n+1]-=G[j][i]*G[i][n+1];}
}
for(int i=1;i<n;i++) {printf("%.7lf\n",G[i][n+1]);}
// printf("G0 = %.6lf\n",G[n][n+1]);
}
int main() {
int n,m;scanf("%d%d",&n,&m);
pw_2[0]=pw2[0]=1;for(int i=1;i<=m;i++)pw2[i]=pw2[i-1]*2%mod,pw_2[i]=pw_2[i-1]*0.5;
for(int i=1;i<=n;i++) {
scanf("%s",s);
for(int j=1;j<=m;j++) {H[i][j]=(H[i][j-1]*2+(s[j-1]=='T'))%mod;}
}
//boader
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++)for(int k=1;k<=m;k++) {
if(Hash(i,1,k)!=Hash(j,m-k+1,m))continue;
G[i][j]+=pw_2[m-k];
// printf("(i=%d,j=%d,k=%d)\n",i,j,k);
}
G[i][n+1]=-1;
}
for(int j=1;j<=n;j++)G[n+1][j]=1;G[n+1][n+2]=1;
Gauss(n+1);
return 0;
}
构造
无标号Multiset构造
- 组合意义:完全背包,即构造\(\mathcal{A}\)里元素所有可能组合。
- 算法:(polya exp & Euler 变换)
\(\xi(A(x))=\prod\limits_{i\ge1}(\dfrac{1}{1-x^i})^{a_i}=\exp (\sum\limits_{j\ge1}\dfrac{A(x^j)}{j})\)
例题:
1.洛谷 P4389 付公主的背包:直接欧拉变换取出系数即可。
2.loj566:多重背包(有取的个数上界,推柿子跟前一题差不多,暂时鸽着)
3.无标号无根树计数:欧拉变换+分治fft/牛顿迭代接方程
ps.总体能掌握推柿子的套路,以及熟悉了解用途即可。
无标号Powerset构造
- 组合意义:01背包
- 算法:(改版欧拉变换,polya指数)
\(\bar{\xi}(A(x))=\prod\limits_{i\ge0}(1+x^i)^{a_i}=\exp (\sum\limits_{i\ge1}(-1)^{j-1}\dfrac{A(x^j)}{j})\)
例题:「SWTR-03」Counting Trees:较板,比较有趣
未完待续……+