Tiw_Air_OAO 数学杂题选讲

伯努利数求自然数幂前缀和

nk=[xk]11nx=k![xk]enx

EGF(指数型生成函数)形式更为常用,不如说 OGF(普通型生成函数)几乎没用。

例:

i=0nik=k![xk]i=0neix=k![xk]e(n+1)x1ex1

后面的分式可以拆成 xex1(只有常数项不为零的形式幂级数才可以求逆)与 e(n+1)x1x 的乘积。

前者与 n 无关,可以预先求出。事实上,这就是伯努利数的 EGF

与斯特林数的异同

斯特林数也可以用于求自然数幂和。

i=0nik=j=0k{kj}(n+1j+1)j!

但其实它们是差不多的!回忆斯特林数的 EGF

i=0+{nk}xnn!=(ex1)kk!

那么:

i=0nik=k![xk]i=0neix=k![xk]i=0n(ex1+1)i=k![xk]i=0nj=0i(ij)(ex1)j=k![xk]j=0n(n+1j+1)(ex1)j=k![xk]j=0n(ex1)jj!(n+1j+1)j!=k![xk]j=0k(ex1)jj!(n+1j+1)j!=j=0k{kj}(n+1j+1)j!

这其中关键的一步就是 j=0n(ex1)jj!(n+1j+1)j!j=0k(ex1)jj!(n+1j+1)j! 的指标代换,这事实上也是斯特林反演的关键。它成立的原因是:ex1 常数项是零,从而当 j>k[xk](ex1)j=0

两个做法在第二步出现分歧,伯努利数直接对原式化简,而斯特林反演则是将关于 ex 的函数代换成 ex1

第二个做法可以避免求逆元。

[NOI Online 2021 提高组] 愤怒的小 N

设集合 S 对应 FS(x)=uSeux ,那么 k![xk]FS(x) 就是 S 中所有数的 k 次幂之和。

我们想要求出长度为 n 的前缀对应的集合 S。由于字符串的特殊性,可以考虑倍增。

分别维护长度为 2t 的前缀 0/1 对应的集合的生成函数 Pt(x),Qt(x),有如下转移式成立:

Pt+1(x)=Pt(x)+e2txQt(x),P0(x)=1Qt+1(x)=Qt(x)+e2txPt(x),Q0(x)=0

仔细瞪一下,作换元 Rt=Pt+QtTt=PtQt 能够给出更简洁的递推式:

Rt+1=(1+e2tx)Rt,R0=1Tt+1=(1e2tx)Tt,T0=1

发现 Rt=i=02kex ,就是自然数幂和,同时 (1e2tx) 常数项为 0 ,因此 Qt(x)0(modxk) (tk),我们只需要求前 k 项。拿 Qtn=(nlen1n0)2bit,得到:

Q(x)=i=0l1[ni=1](1)count((n(nin0)2)e(n(nin0)2)xQi(x).

事实上指标 i 的有效范围只到 k,直接 O(k3) 算出来。另一方面 P(x) 的自然数幂和可以拉格朗日插值,最终复杂度是 O(logn+k3)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,k;
char s[500005];
const int md=1e9+7;
inline int pwr(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=1ll*res*x%md;
		x=1ll*x*x%md;y>>=1;
	}
	return res;
}
int a[505],fac[505],ifac[505],inv[505],Pwr[500005];
int T[505][505],e[505],t[505],tmp[505];
inline void mul(int *a,int *b,int *c){
	for(int i=0;i<k;i++)c[i]=0;
	for(int i=0;i<k;i++){
		for(int j=0;i+j<k;j++)c[i+j]=(c[i+j]+1ll*a[i]*b[j])%md;
	}
}
int pre[505],suf[505];
inline int lagrange(int *y,int x){
	int res=0;pre[0]=x;suf[k+1]=1;
	for(int i=1;i<=k;i++)pre[i]=1ll*pre[i-1]*(x-i)%md;
	for(int i=k;i>=1;i--)suf[i]=1ll*suf[i+1]*(x-i)%md;
	for(int i=0;i<=k;i++){
		int tmp=1ll*ifac[i]*ifac[k-i]%md*((k-i)&1?md-1:1)%md;
		if(i)tmp=1ll*tmp*pre[i-1]%md;
		if(i!=k)tmp=1ll*tmp*suf[i+1]%md;
		res=(res+1ll*y[i]*tmp)%md;
	}
	return res;
}
inline void init(){
	Pwr[0]=fac[0]=fac[1]=inv[0]=inv[1]=ifac[0]=ifac[1]=1;
	for(int i=2;i<=k;i++)fac[i]=1ll*fac[i-1]*i%md;
	for(int i=2;i<=k;i++)inv[i]=1ll*(md-md/i)*inv[md%i]%md;
	for(int i=2;i<=k;i++)ifac[i]=1ll*ifac[i-1]*inv[i]%md;
	for(int i=1;i<=n;i++)Pwr[i]=2ll*Pwr[i-1]%md;
	T[0][0]=1;
	for(int i=1;i<k;i++){
		for(int j=1;j<k;j++)e[j]=1ll*ifac[j]*pwr(Pwr[i-1],j)%md;
		for(int j=1;j<k;j++)e[j]=(md-e[j])%md;mul(T[i-1],e,T[i]);
//		cout<<i<<endl;
//		for(int j=0;j<k;j++)cout<<T[i][j]<<" ";cout<<endl;
	}
	int sum=0,op=-1;
	for(int i=n-1;i>=k;i--){
		if(s[i])sum=(sum+Pwr[i])%md,op*=-1;
	}
	for(int i=k-1;~i;i--){
		if(s[i]){
			for(int j=0;j<k;j++)e[j]=1ll*ifac[j]*pwr(sum,j)%md;
			mul(T[i],e,tmp);
			for(int j=0;j<k;j++)t[j]=(t[j]+1ll*op*tmp[j]+md)%md;
			sum=(sum+Pwr[i])%md;op*=-1;
		}
	}
	for(int i=0;i<k;i++)t[i]=1ll*t[i]*fac[i]%md;
//	for(int i=0;i<k;i++)cout<<t[i]<<" ";cout<<endl;
	for(int i=0;i<k;i++)scanf("%d",&a[i]);
	for(int i=0;i<k;i++){
		tmp[0]=(i==0);
		for(int j=1;j<=k;j++)tmp[j]=(tmp[j-1]+pwr(j,i))%md;
		t[i]=1ll*(t[i]+lagrange(tmp,sum-1))%md*pwr(2,md-2)%md;
	}
//	for(int i=0;i<k;i++)cout<<t[i]<<" ";cout<<endl;
	int res=0;
	for(int i=0;i<k;i++)res=(res+1ll*a[i]*t[i])%md;
	printf("%d\n",res);
}
int main(){
	scanf("%s",s);n=strlen(s);
	reverse(s,s+n);for(int i=0;i<n;i++)s[i]-='0';
	scanf("%d",&k);init();


	return 0;
}


hdu6593 Coefficient

可以换元 g(x)=1bf(x+x0)=1c+eax,因此我们就假定 b=1,d=x0=0 吧。

根据 f(x)=aeax(c+eax)2,可以想到接下来的形式必然是 fjejax(c+eax)j+1 。系数的变化规律是:

derivative's fj=ja(fjfj1)

a 是统一的,可忽略之,最后答案乘 an 。初值 f0=1 。最后的答案就是 1n!j0fj(c+1)j+1

考虑系数的 “移动”(因为是线性变换),可见路径权值是 (1)ki=1kiqi,其中 qi0qi=n,贡献到 fk 。这就可以用 OGF 轻易描述了:

fk=[xn](1)kj=1kjx1jx

我不熟悉它,所以我没能指出:j=1kx1jx第二类斯特林数 在第 k 列上的 OGF 。其实想想 i=1kiqi 不就是每次要么放进新盒子、要么放进已有的盒子,第二类斯特林数吗!

“你看到的只是表象。” —— 太阳神 Tiw-Air-OAO

因此 fk=k!(1)k{nk} 即得。用斯特林数在第 n 行上的 OGF 去算即可。

再或者,我们可以换一种刺杀的方式。直接从生成函数的角度考虑它,答案就是

[xn]1c+eax

吸取 “Binomial Sum” 的经验,只需把 F(x)=11+c+x 展开成

F(x)=(c+1)1i=0n(1)i(c+1)i

我们先算出

[xn](eax1)t=i=0t(ti)(1)ti(ai)n

Remark:把 a 去掉后,其实 1t!(ex1)t 就是第二类斯特林数在第 t 列上的 EGF 。因此我们得到的也就是第二类斯特林数在第 n 行上的 OGF

直接得到

ans=an[(c+1)1t=0n(c+1)ti=0t(ti)(1)iin]

系数 i=0t(ti)(1)iin 是简单卷积,然后对 q(c+1)1 多点求值。复杂度 O(nlogn+qlog2n)

点击查看代码

#4126. 国王奇遇记加强版之再加强版

求:

(i=1nim ×mi)mod(109+7)  (n109,m500000)

m![xm]eix=imm 放到指数外,有:

ans=i=1nim×mi=m![xm]i=1neixmi=m![xm]1mn+1e(n+1)x1mex.

其中分母是常多项式,可以分析到答案是关于 n+1 的多项式。令 F(x)=11mex,则:

ans=m!(fmmn+1i=0m(n+1)ii!fmi).

可见,若令:

P(x)=i=0mxifmii!

则:

ans=m!(P(0)mn+1P(n+1))

但是我们对 P,甚至对 f 一无所知。怎么办?其实 n 较小的时候,我们可以用答案最初的式子算出来,继而得到若干 P(t)P(0) 的值的线性关系。再者,P(x)m 次多项式,其 m+1 阶差分为 0。所以:

Δm+1P(0)=i=0m+1(1)m+1i(m+1i)P(i).

利用已知线性关系解出 P(0),得到点值,Lagrange 差值。线性筛自然数幂、线性求一堆逆元,精细实现可以做到 O(m)

#6718. 九个太阳「弱」化版

给定 n,k ,满足 k2 的幂,求

k|i,0in(ni)

998244853 取模。

发现:

0in(ni)=(1+x)n

进而 0in[k|i](ni) 就是 (1+x)nk 的倍数次项,即 (1+x)nmod(xk1) 的常数项,对 (1+x)n 做长度为 k 的循环卷积即可。

「Karatsuba 算法」

设要计算 2n1 次多项式 F(x)G(x) 的卷积,令 F(x)=F0(x)+xnF1(x)G(x)=G0(x)+xnG1(x),则:

F(x)G(x)=(F0(x)+xnF1(x))(G0(x)+xnG1(x))=F0(x)G0(x)+xn((F0(x)+F1(x))(G0(x)+G1(x))F0(x)G0(x)F1(x)G1(x))+x2nF1(x)G1(x).

需要三次卷积,每次卷积递归计算。复杂度 T(n)=3T(n/2)+O(n)=O(nlog23)

不过还是过不了这题,还是要写 MTTlong doubleFFT 并每次取模。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int md=998244853,wei=15;
int R[1000005];
int pw(int x,int y,int md){
	int ans=1;
	while(y){
		if(y&1)
			ans=1ll*ans*x%md;
		x=1ll*x*x%md;
		y>>=1;
	}
	return ans;
}
const long double pi=acos(-1.0);
struct CP{
	long double a,b;
	CP(){}
	CP(long double c,long double d){
		a=c;b=d;
	}
	friend CP operator + (const CP &x,const CP &y){
		return CP(x.a+y.a,x.b+y.b);
	}
	friend CP operator - (const CP &x,const CP &y){
		return CP(x.a-y.a,x.b-y.b);
	}
	friend CP operator * (const CP &x,const CP &y){
		return CP(x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a);
	}
	friend CP operator ~ (const CP &x){
		return CP(x.a,-x.b);
	}
};
void fft(CP a[],int len,int type){
	for(int i=0;i<len;i++)
		if(i<R[i])swap(a[i],a[R[i]]);
	for(int mid=1;mid<len;mid*=2){
		CP wn=CP(cos(pi/mid),type*sin(pi/mid));
		for(int j=0;j<len;j+=mid*2){
			CP w=CP(1,0);
			for(int k=0;k<mid;k++,w=w*wn){
				CP x=a[j+k],y=w*a[j+k+mid];
				a[j+k]=x+y;a[j+k+mid]=x-y;
			}
		}
	}
	if(type==1)return;
	for(int i=0;i<len;i++){
		a[i].a/=len;a[i].b/=len;
	}
}
CP A[1000005],B[1000005],C[1000005],D[1000005];
inline void mul(int a[],int b[],int n){
	int len=n;
	for(int i=0;i<len;i++){
		R[i]=(R[i>>1]>>1)|((i&1)*(len/2));
		A[i]=CP(a[i]>>wei,a[i]&((1<<wei)-1));
		B[i]=CP(b[i]>>wei,b[i]&((1<<wei)-1));
	}
	fft(A,len,1);fft(B,len,1);
	for(int i=0;i<len;i++){
		int j=((len-i)&(len-1));
		CP a0=(~A[j]+A[i])*CP(0.5,0),a1=(~A[j]-A[i])*CP(0,0.5),b0=(~B[j]+B[i])*CP(0.5,0),b1=(~B[j]-B[i])*CP(0,0.5);
		C[i]=a0*b0+a1*b1*CP(0.0,1.0);D[i]=a0*b1+a1*b0;
	}
	fft(C,len,-1);fft(D,len,-1);
	for(int i=0;i<n;i++){
		long long tmp1=(long long)(C[i].a+0.5)%md,tmp2=(long long)(D[i].a+0.5)%md,tmp3=(long long)(C[i].b+0.5)%md;
		a[i]=((tmp1<<(wei*2))+(tmp2<<wei)+tmp3)%md;
	}
}
int ans[1000005];
void solve(long long n,int k){
	if(n==1){
		ans[0]++;ans[1%k]++;
		return;
	}
	solve(n/2,k);
	mul(ans,ans,k);
	if(n&1){
		int tmp=ans[k-1];
		for(int i=k-1;i>=1;i--)ans[i]=(ans[i]+ans[i-1])%md;
		ans[0]=(ans[0]+tmp)%md;
	}
}
int main(){
	long long n;
	int k;
	scanf("%lld%d",&n,&k);
	solve(n,k);
	printf("%d",ans[0]);
	return 0;
}

hdu6355 fireflies

给定 p1...n,以及 Pi 个点 (x1,x2,...,xn)0xi<pi

一只萤火虫可以从 (0,0,...,0) 开始起飞,每次飞翔使得某个坐标增加 1,并点亮路过的点,直到飞到终点 (p11,p21,...,pn1)

请求出最少需要多少只萤火虫才能点亮所有点。n32pi109

转最大反链。以下为方便起见,记 ai=pi1

上述问题可以形式化成:给定包含 n 种元素的多重集 S,其中 i 种元素出现 ai 次。定义子集之间的偏序为 ,求最大反链。

定理:M=ai2,则选择所有大小为 M 的子集即达到最大反链。

我们称子集链 T1T2Tk 是对称链,当且仅当 |Ti|+1=|Ti+1||T1|+|Tk|=ai 。注意 k 可能为 1,此时 |T1|=a2(2a)

引理: 存在对 S 进行 “对称链分解” 的方式,使得 S 的每个子集在恰好一条链中出现。

证明: 归纳构造。当 n=0T1= 是一条链。然后每次加入 a 个元素 x,对于原本的对称链 T1T2Tk,可将其变为 min{a+1,k} 条对称链。

{T1TkTk+{1×x}Tk+{a×x}T1+{1×x}Tk1+{1×x}Tk1+{2×x}Tk1+{a×x}T1+{2×x}Tk2+{2×x}Tk2+{3×x}Tk2+{a×x}T1+{a×x}T2+{a×x}Tka+{a×x}

可以画图来直观感受它。原本的链就是 Ti=(0,i),是 y 轴上的点,而现在 [0,a] 成为 x 轴可选值。这个划分方式就是,先从 (0,1) 开始,进行 “无脑搜索”——往上一直走,直到下一个点已走过,然后往右一直走。再从 (1,1),(2,1),,(min{a,k},1) 分别进行 “无脑搜索”。

不难验证其仍为对称链。

注意到,大小为 M=ai2 的子集在每个对称链里都出现了恰好一次。因此,这样的集合的数量就是最长反链的长度(我们构造出了其对应的链覆盖)。

剩下的部分就是折半、容斥,求 i=1nxi=M,xiai 的整数解的个数。重点其实在于 Lemma

s{1,2,...,n}(1)|s|(Mispi+n1n1)=iSjTαiβj(Mij+n1n1)=t=0n1iSαi(Mit)jTβj(n1jn1t)

点击查看代码

ioi2021 集训队互测 子集匹配

U={1,2,3,,n},记 Fk={S|SU,|S|=k} 。若 TFk1,SFk 满足 TS 则二者之间连边。请构造大小为 |Fk| 的匹配。保证 2n2k>n

用对称链构造即可。

[WC2021] 斐波那契

定义 F0=aF1=bFi=(Fi1+Fi2)modmi2)。

现在给定 n 组询问,对于每组询问请找到一个最小的整数 p,使得 Fp=0

1n,m105

一个初等的思考方法是:转移矩阵的幂可以用斐波那契数填出。

如果互质性比较好,逆元存在,那么直接分离开来变成 ab=fnfn1 。我们继续这个思路(分离 a,b{fn} ):

考虑移项,并给 a,b,m 同时除掉 gcd(a,b,m),得到:

afn1=(b)fn(modm)

此时 gcd(a,b,m)=1,但 gcd(a,m),gcd(b,m) 仍可以 >1

由于 gcd(fn1,fn)=1 ,此时应有:

gcd(fn,m)=gcd(a,m)=pgcd(fn1,m)=gcd(b,m)=q

(符号问题请大家自觉忽略! 换个元 b=b 就解决了! )

此时等式两边以及模数同除 pq 即可实现互质,于是就分离开来了!

那么在 m 处塞个三元组 (p,q,fnfn1modmpq) ,之后直接查询即可。

斐波那契数的循环节是 O(n) 的。那么该算法预处理的复杂度为 O(σ(m)×logm) ,其 中 σ(m) 是因子和,查询的复杂度是 O(nlogm)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
void exgcd(int a,int b,int &x,int &y){
	if(!b){x=1;y=0;return ;}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
inline int Inv(int x,int md){
	int a,b;exgcd(x,md,a,b);
	return (a%md+md)%md;
}
struct node{
	int x,y,z;
	node(int _x,int _y,int _z){
		x=_x;y=_y;z=_z;
	}
	inline bool operator <(const node &b)const{
		if(x!=b.x)return x<b.x;
		if(y!=b.y)return y<b.y;
		return z<b.z;
	}
};
map<node,int> mp[100005];
int gcd(int x,int y){
	return y?gcd(y,x%y):x;
}
inline void init(){
	for(int i=2;i<=m;i++){
		if(m%i==0){
			int x=1,y=0;
			for(int j=0;;j++){
				if(x&&y){
					int p=gcd(x,i),q=gcd(y,i),m1=i/p/q;
					int k=1ll*(y/q)*Inv(x/p,m1)%m1;
					if(!mp[i].count(node(k,p,q)))mp[i][node(k,p,q)]=j;
				}
				swap(x,y);y=(x+y)%i;
				if(x==1&&y==0)break;
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);init();
	for(int i=1;i<=n;i++){
		int a,b;scanf("%d%d",&a,&b);
		b=(m-b)%m;
		if(a==0)puts("0");
		else if(b==0)puts("1");
		else {
			int d=gcd(gcd(a,b),m),m1=m/d;a/=d,b/=d;
			int p=gcd(a,m1),q=gcd(b,m1),m2=m1/p/q;a/=p,b/=q;
			int k=1ll*a*Inv(b,m2)%m2;
			if(mp[m1].count(node(k,q,p)))printf("%d\n",mp[m1][node(k,q,p)]);
			else puts("-1");
		}
	}


	return 0;
}



[AGC021F] Trinity

现有一个 NM 列的、仅包含黑白格的表格,左上为 (1,1) ,右下为 (N,M) 。 对于一个表格,设长度为 N 的数列 A ,长度为 M 的数列 BC 分别表示:

  • Ai 表示第 i 行第一个黑格的列号。若不存在则为 M+1
  • Bi 表示第 i 列第一个黑格的行号。若不存在则为 N+1
  • Ci 表示第 i 列最后一个黑格的行号。若不存在则为 0

现请你求出所有的 2NM 种表格中,不同的数列三元组 (A,B,C) 的个数对 998244353 取模的结果。

1N8×103,1M200 。 

我们跳过 dp 推理,得到 dp 转移式如下

dpm,n=(1+(n+12))dpm1,n+i<n(n+2i)dpm1,i

最后答案为 i=0n(ni)dpm,i

自然考虑写成 EGF: 记 Fm(x)=n0dpm,nn!xn

那么转移式可以改写成如下的微分方程:

Fm=exFm1+(2exx2)Fm1+(exx1)Fm1

之后可以考虑将 Fm 写成 i=1mj=1mfm,i,jxiejx 的形式,这样转移的代价就降到了 O(m2)

这样的时间复杂度其实是 O(m3)n 无关。

点击查看代码
#include <bits/stdc++.h>
#define mod 998244353
using namespace std;
inline int read(){
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9')f=(c=='-')?-1:f,c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x*f;
}
inline int M(int x){return x>=mod?x-mod:x;}
int fsp(int bs,int p){
    int rt=1;
    while(p){
        if(p&1)rt=1ll*rt*bs%mod;
        bs=1ll*bs*bs%mod,p>>=1;
    }
    return rt;
}
inline int C2(int x){return x*(x-1)/2;}
int fac[10004],caf[10004];
inline int C(int x,int y){
	return 1ll*fac[x]*caf[y]%mod*caf[x-y]%mod;
}
inline void init(){
    int lim=10000;
    fac[0]=1;
    for(int i=1;i<=lim;i++)fac[i]=1ll*fac[i-1]*i%mod;
    caf[lim]=fsp(fac[lim],mod-2);
    for(int i=lim;i>=1;i--)caf[i-1]=1ll*caf[i]*i%mod;
}
int rtt[20004],O[20004],ny;
inline void getrtt(int w,int len){
    for(int i=1;i<len;i++)rtt[i]=rtt[i>>1]>>1|((i&1)<<w);
    for(int l=2;l<=len;l<<=1)O[l]=fsp(3,(mod-1)/l);
    ny=fsp(len,mod-2);
}
struct Poly{
    int x[20004];
    int &operator[](int p){return x[p];}
    inline void ntt(int len){
        for(int i=1;i<len;i++)if(rtt[i]>i)swap(x[rtt[i]],x[i]);
        for(int l=2;l<=len;l<<=1){
            for(int i=0,m=l>>1;i<len;i+=l){
                for(int j=i,tO=1,t;j<i+m;j++){
                    t=1ll*tO*x[j+m]%mod,tO=1ll*tO*O[l]%mod;
                    x[j+m]=M(x[j]-t+mod),x[j]=M(x[j]+t);
                }
            }
        }
    }
    inline void idft(int len){
        ntt(len),reverse(x+1,x+len);
        for(int i=0;i<len;i++)x[i]=1ll*x[i]*ny%mod;
    }
}f,g,tf;
int n,m;
int main(){
    n=read();m=read();
    init();
    int w=1,len=2;
    while(len<2*n+1)len<<=1,++w;
    for(int i=1;i<=n;i++)g[i]=caf[i+2];
    getrtt(w-1,len),g.ntt(len);
    tf[0]=f[0]=1;
    for(int i=1;i<=m;i++){
        f.ntt(len);
        for(int j=0;j<len;j++)f[j]=1ll*f[j]*g[j]%mod;
        f.idft(len);
        for(int j=0;j<=n;j++){
            tf[j]=M(1ll*f[j]*fac[j+2]%mod+1ll*tf[j]*(1+j+C2(j))%mod);
            f[j]=1ll*tf[j]*caf[j]%mod;
        }
        for(int j=n+1;j<len;j++)f[j]=0;
    }
    int res=0;
    for(int i=0;i<=n;i++)res=M(res+1ll*C(n,i)*tf[i]%mod);
    printf("%d\n",res);
    return 0;
}


[2019 江苏省队集训] Day1 T1 光影交错

两个人玩游戏,有 pL 的概率 Alice 获胜,有 pD 的概率 Bob 获胜,还有 1pLpD 的概率两人平局。0pL,pDpL+pD1

当然两人可以一直玩下去,于是每局游戏后 p 的概率两人停止游戏,0<p1

问游戏停止时,有多少时刻 Alice 的胜利局数严格多余 Bob 的胜利局数。

原题容许较大的精度误差,但是我们可以给出其封闭形式!

首先特判 p=1 ,以下假设 p(0,1) ,另还有 pL,pD,pL+pD[0,1]

pE=1pLpD[0,1] ,则答案为:

l>d0,e0(l+d+el,d,e)pLlpDdpEe(1p)l+d+e1

作换元 qp(1p) :

11pl>d0,e0qLlqDdqEe=11pl>d0(l+dl)qLlqDde0(l+d+ee)qEe=11pl>d0(l+dl)qLlqDd1(1qE)l+d+1

注意 p>0 ,因此 qE=pE(1p)<1 在收敛域内,再作换元 tq1qE :

1(1p)(1qE)l>d0(l+dl)tLltDd

之前我的推法是直接处理 SD(l)=d=0l1(l+dl)tDd ,但总感觉不是很自然。

现在想想,可以尝试作换元 δ=ld10 使得两元独立:

1(1p)(1qE)δ,d0(2d+δ+1d)(tLtD)dtLδ+1

s=tLtD ,则 d0(2d+δ+1d)sd=(114s2s)δ+1/(14s) ,可以通过拉格朗日反演推得,收敛域为 [1/4,1/4)

由于 tL+tD=(1p)(pL+pD)1(1p)(1pLpD)=(1p)(pL+pD)(1p)(pL+pD)+p<1 ,由均值不等式可得 s=tLtD<1/4 ,又有 s0

但是这个形式并无法兼容 s=0 的情况,我的解决方案是,先假设 s>0 ,最后再代入 s=0 来验证形式的统一。所以:

1(1p)(1qE)δ0(114s2s)δ+114stLδ+1

然后一通分析 114s2stL 收敛域 (0,1) 内 (由于条件 tL+tD<1 成立) :

114s2stL×114s(1p)(1qE)×11114s2stL

我们至少得到了一个封闭形式。但很遗恫的是,这个封闭形式对某些边界值末定义。尝试 继续化简:

114s(1p)(1qE)×114s2tD1+14s=114s(1p)(1qE)×(114s)(2tD114s)(2tD1)2(14s)=114s(1p)(1qE)×114s2tL2tD+2tL2=114s(1p)×14s+2tL12(1qE)2qD2qL=14s+2tL1214s(1p)p

最后这个形式就比较良好了。当然,也可以把换过的元给代回去,得到完全用题目中的变量表达的式子:

12p2p2(1+pLpDp1p(p1p+pL+pD)24pLpD)

点击查看代码

posted @   一粒夸克  阅读(258)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示

目录导航