PGF&&DGF 学习笔记
PGF 学习笔记
性质与定义
基本全是口胡的,有错给我说。
全称概率型生成函数,基本形式如:
- 对于一个离散的指数型生成函数来讲(以下也有同样要求),
F(1)=1 。
理由:不难发现
实际上,离散好像不是硬性要求,但是不离散好像没什么意义。
理由:不难发现
理由:说实话不知道有啥大用,但是可以推导之后的式子,
理由:
- 方差
V(x)=E(X^2)-E(X)^2=F^{''}(1)+F^{'}(1)-F^{'}(1)^2 。
理由后面会补,貌似《具体数学》第八章上面有。
有多项式和期望基础,可以直接看题了。
做题
注:某些题后面可能会有带误导性的口胡,算是一些自己的理解,最好别信。
P4548
比较牛的套路题,有助于认识 Trick。
记
这种方法是我最开始没有想到的,因为我想一步到位直接设出状态,太贪心了。
有:
理由:先看
然后展开来:
对于两边同时求导:
将
而由之前的性质,我们知道
但是
接下来的转换比较神秘了,重要的套路。
我们构造出强制满足条件的生成函数,也即构造一组必然结束的情况。
不难发现是
发现这玩意有点问题,因为可能会提前结束,也即:还没拼接完
对于这种情况,我们考虑对
我们用
理由:发现提前结束当且仅当目前串的末尾是原串的 Border,所以枚举 Border 长度即可处理提前结束的情况。
使用之前的思路,代入
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int mod=10000;
struct modint {
int val;
static int norm(const int& x) { return x < 0 ? x + mod : x; }
modint() : val(0) {}
modint(const int& m) : val(norm(m)) {}
modint& operator+=(const modint& o) { return val = (1ll * val + o.val) % mod, *this; }
modint& operator*=(const modint& o) { return val = static_cast<int>(1ll * val * o.val % mod), *this; }
modint operator+(const modint& o) const { return modint(*this) += o; }
modint operator*(const modint& o) const { return modint(*this) *= o; }
};
const int N=2e5+10;
int n,t,a[N],nex[N],m;modint ans;
inline modint expow(modint x,int y){modint res=1;for(;y;y>>=1){if(y&1)res*=x;x*=x;}return res;}
int main(){
scanf("%d%d",&n,&t);
while(t--){
scanf("%d",&m);ans=0;
for(int i=1;i<=m;++i) scanf("%d",&a[i]);
for(int i=1;i<=m;++i) nex[i]=0;
for(int i=1,j=0;i<m;++i){
while(j&&a[j+1]!=a[i+1]) j=nex[j];
nex[i+1]=(j+=a[j+1]==a[i+1]);
}
int now=m;
while(now){ans+=expow(n,now);now=nex[now];}
printf("%04d\n",ans.val);
}
return 0;
}
嗯注意
然后生成函数特定情况下是可以代值的,但是你要保证函数时收敛的,比如
其它例题
ARC136F,P3706,这篇文章,还可以去看一下「2013 Multi-University Training Contest 5」Dice。
然后讲解的话,这篇和这篇可以专门去看一下,如果想更深入的了解可以去翻 zhihu,上面有很多深刻的文章,只是 OI 里面不一定 pratical。
然后 ZJOI 的开关也是有 PGF 做法的,感觉稍微有点吃数学能力。
DGF 学习笔记
PGF 习题咕咕了,为了防止省选之前的时间太颓写一下 DGF 学习笔记。
感觉几乎用不上啊,大部分时候是用来分析问题而不是提供一种强力的计算套路。
不过这篇文章 link 挺有趣的。
这篇和这篇写得也比较深刻!
总的来说是比较精巧的东西吧!
性质与定义
也基本是口胡。
全称是狄利克雷生成函数,形如
一般是用来处理积性函数,也即
相当的优雅。
考虑将部分经典的积性函数转化成 DGF 形式来加深理解。
黎曼函数 \zeta
不难发现黎曼函数是 DGF 下的
莫比乌斯函数 \mu
不难发现
这里就要谈一下 DGF 卷积的直观意义了:
所以
欧拉函数 \varphi
如果你往上面看一眼,不难发现
幂函数 I_k
联系上面
其实可以知道这玩意的作用之一是使数论函数的性质更加具体,从而弥补你天生较弱的注意力。
有例题:
相关应用
简单的数学题
算了还是推一下前面的吧。
记
考虑对
考虑套路地找到一个数论函数
于是有
感觉对我这种暴躁老哥+天生注意力涣散的人很友好。
给代码吗?给一个吧?
#include<bits/stdc++.h>
#define int __int128
#define maxn 5000000
using namespace std;
int cnt,prime[maxn+10],phi[maxn+10],sum[maxn+10],p,n,ans;
map < int ,int > m;
bool flag[maxn+10];
void predo(){
flag[1]=1;
phi[1]=1;
for(int i=2;i<=maxn;++i){
if(!flag[i]) prime[++cnt]=i,phi[i]=i-1;
// cout<<(long long)i<<endl;
for(int j=1;j<=cnt&&i*prime[j]<=maxn;++j){
flag[i*prime[j]]=1;
if(i%prime[j]==0){
(phi[i*prime[j]]=phi[i]*prime[j])%=p;
break;
}
(phi[i*prime[j]]=phi[i]*phi[prime[j]])%=p;
}
}
// cout<<"QAQ"<<endl;
for(int i=1;i<=maxn;++i) (sum[i]=sum[i-1]+(i*i%p*phi[i])%p)%=p;
}
int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
return x * f;
}
inline int sum2(int n){
n%=p;
return n*(n+1)*(2*n+1)/6%p;
}
inline int sum3(int n){
n%=p;
return (n*(n+1)/2%p)*(n*(n+1)/2%p)%p;
}
int getphi(int n){
if(n<=maxn) return sum[n];
if(m[n]) return m[n];
int ans=sum3(n);
for(int i=2,j;i<=n;i=j+1){
j=n/(int)(n/i);
ans-=(sum2(j)-sum2(i-1)+p)%p*getphi(n/i)%p;
(ans+=p)%=p;
}
return m[n]=ans;
}
signed main(){
p=read();
n=read();
predo();
for(int i=1,j;i<=n;i=j+1){
j=(n/(n/i));
ans+=sum3(n/i)*(getphi(j)-getphi(i-1)+p)%p;
ans%=p;
}
long long tmp=ans;
printf("%lld\n",tmp);
return 0;
}