【线性代数/生成函数推导】CF947E Perpetual Subtraction

【题目】
CF
初始有一个数字 x ∈ [ 0 , n ] x\in [0,n] x[0,n],给出它取每一个值的概率。接下来进行 m m m轮游戏,每轮游戏等概率选择一个数 y ∈ [ 0 , x ] y\in[0,x] y[0,x],然后令 x = y x=y x=y,求最终 x = 0 … n x=0\dots n x=0n每个值的概率分别是多少,答案对 998244353 998244353 998244353取模。
n ≤ 1 0 5 , m ≤ 1 0 18 n\leq 10^5,m\leq 10^{18} n105,m1018

【解题思路】
首先写出一个朴素的 DP \text{DP} DP,令 f i , j f_{i,j} fi,j表示到第 i i i轮,数字为 j j j的概率,那么有:
f i , j = ∑ k = i n f i − 1 , k k + 1 f_{i,j}=\sum_{k=i}^n\frac {f_{i-1,k}} {k+1} fi,j=k=ink+1fi1,k
将这个东西写成转移矩阵的形式,可以发现这是一个上三角矩阵,且系数十分优美,第 i i i列的非零元素为 1 i \frac 1 i i1,共 n + 1 n+1 n+1列。

然后我们也可以很轻松地得到矩阵的特征根方程和其特征根,即:
f ( λ ) = ∏ i = 1 n + 1 ( λ − 1 i ) f(\lambda)=\prod_{i=1}^{n+1}(\lambda -\frac 1 i) f(λ)=i=1n+1(λi1)
于是可以大概通过常系数线性递推做到 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)吧。

不会线代的我滚粗了。

上面当我什么都没写吧。我们考虑令 f i f_i fi的生成函数为 F ( x ) F(x) F(x) f i + 1 f_{i+1} fi+1的生成函数为 F ^ ( x ) \hat{F}(x) F^(x)则有:
F ( x ) = ∑ i = 0 n f i x i F(x)=\sum_{i=0}^n f_ix_i F(x)=i=0nfixi
F ^ ( x ) = ∑ i = 0 n x i ∑ j = i n f j j + 1 = ∑ j = 0 n f j j + 1 ∑ i = 0 j x i = ∑ j = 0 n f j j + 1 ⋅ x j + 1 − 1 x − 1 = 1 x − 1 ∑ j = 0 n f j ∫ 1 x t j d t = ∫ 1 x F ( t ) d t x − 1 \begin{aligned} \hat{F}(x) =&\sum_{i=0}^nx^i\sum_{j=i}^n \frac {f_j} {j+1}\\ =& \sum_{j=0}^n \frac {f_j} {j+1} \sum_{i=0}^j x^i\\ =& \sum_{j=0}^n\frac {f_j} {j+1} \cdot \frac {x^{j+1}-1}{x-1}\\ =& \frac 1 {x-1} \sum_{j=0}^n f_j \int_{1}^x t^j \mathrm{d}t\\ =& \frac {\int_{1}^x F(t)\mathrm{d} t}{x-1} \end{aligned} F^(x)=====i=0nxij=inj+1fjj=0nj+1fji=0jxij=0nj+1fjx1xj+11x11j=0nfj1xtjdtx11xF(t)dt
这个形式并不那么优美,于是令 G ( x ) = F ( x + 1 ) G(x)=F(x+1) G(x)=F(x+1),则有:
∫ 1 x + 1 F ( t ) d t x + 1 − 1 ⇒ ∫ 0 x G ( t ) d t x \frac {\int_{1}^{x+1} F(t)\mathrm{d} t}{x+1-1} \Rightarrow \frac {\int_{0}^x G(t)\mathrm{d} t}{x} x+111x+1F(t)dtx0xG(t)dt
好像就很优美了,可以发现 g ^ i = g i i + 1 \hat{g}_i=\frac {g_i} {i+1} g^i=i+1gi,那么实际上就是每个位置乘了一个快速幂而已。
接下来考虑 f , g f,g f,g的关系,我们有:
∑ i = 0 n g i x i = ∑ i = 0 n f i ( x + 1 ) i = ∑ i = 0 n f i ∑ j = 0 i ( i j ) x j \sum_{i=0}^n g_ix^i=\sum_{i=0}^n f_i(x+1)^i=\sum_{i=0}^nf_i\sum_{j=0}^i {i \choose j} x^j i=0ngixi=i=0nfi(x+1)i=i=0nfij=0i(ji)xj
比较系数后可以得到:
g i = ∑ j = i n ( j i ) f j g_i=\sum_{j=i}^n {j\choose i}f_j gi=j=in(ij)fj
然后二项式反演一下:
f i = ∑ j = i n ( − 1 ) j − i ( j i ) g j f_{i}=\sum_{j=i}^n (-1)^{j-i} {j\choose i} g_j fi=j=in(1)ji(ij)gj
上面两个东西将系数拆开以后移下项就是一个卷积的形式了,以第二个为栗子:
i ! f i = ∑ j = i n ( − 1 ) j − i ( j − i ) ! j ! g j i!f_i=\sum_{j=i}^n \frac {(-1)^{j-i}} {(j-i)!} j!g_j i!fi=j=in(ji)!(1)jij!gj
这个东西怎么卷呢?一种很优秀的方式是考虑对应位置。比如上面这个东西,我们设 j − i j-i ji的部分是 A A A j j j的部分是 B B B。那么先看第 n n n项的对应,那么第 n n n项是由 A A A的第 0 0 0个位置和 B B B的第一个位置相乘贡献的。第 n − 1 n-1 n1项是由 A A A的第 1 1 1个位置和 B B B的第 n n n个位置, A A A的第 0 0 0个位置和 B B B的第 n − 1 n-1 n1个位置贡献。也就是说是一个小对应小,大对应大的卷积,我们需要将一个部分翻转。

A A A翻转后将 A , B A,B A,B进行卷积,再看贡献到的位置,不难发现原来第 n n n项到了 2 n 2n 2n的位置,第 n − 1 n-1 n1项到了 2 n − 1 2n-1 2n1的位置。于是将数组向左平移 n n n位即是答案了。

那么我们就根据上面两条柿子,先用初始的 f 0 f_0 f0求出 g 0 g_0 g0,然后对 g g g的每一位乘个快速幂,再用 g m g_m gm求出 f m f_m fm即可。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=262333,mod=998244353,G=3;

namespace IO
{
	ll read()
	{
		ll ret=0;char c=getchar();
		while(!isdigit(c)) c=getchar();
		while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
		return ret;
	}
	void write(int x){if(x>9)write(x/10);putchar(x%10^48);}
	void writesp(int x){write(x);putchar(' ');}
}
using namespace IO;

namespace Math
{
	int inv[N],fac[N],ifac[N];
	int upm(int x){return x>=mod?x-mod:(x<0?x+mod:x);}
	void up(int &x,int y){x=upm(x+y);}
	int mul(int x,int y){return 1ll*x*y%mod;}
	ll qpow(ll x,ll y){ll res=1;for(;y;y>>=1,x=x*x%mod)if(y&1)res=res*x%mod;return res;}
	ll getinv(int x){return qpow(x,mod-2);}
	void initmath()
	{
		inv[0]=inv[1]=1;for(int i=2;i<N;++i) inv[i]=mul(mod-mod/i,inv[mod%i]);
		fac[0]=1;for(int i=1;i<N;++i) fac[i]=mul(fac[i-1],i);
		ifac[N-1]=getinv(fac[N-1]);for(int i=N-2;~i;--i)ifac[i]=mul(ifac[i+1],i+1);
	}
	int m,L,rev[N];
	void ntt(int *a,int n,int op)
	{
		for(int i=0;i<n;++i) if(i<rev[i]) swap(a[i],a[rev[i]]);
		for(int i=1;i<n;i<<=1)
		{
			int wn=qpow(G,(mod-1)/(i<<1));
			if(!~op) wn=getinv(wn);
			for(int j=0;j<n;j+=i<<1)
			{
				int w=1;
				for(int k=0;k<i;++k,w=mul(w,wn))
				{
					int x=a[j+k],y=mul(w,a[i+j+k]);
					a[j+k]=upm(x+y);a[i+j+k]=upm(x-y);
				}
			}
		}
		if(!~op)for(int i=0,iv=getinv(n);i<n;++i)a[i]=mul(a[i],iv);
	}
	void reget(int n)
	{
		for(m=1,L=0;m<n;m<<=1,++L);
		for(int i=0;i<m;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
	}
	void polymult(int *a,int *b,int *c,int dega,int degb)
	{
		static int A[N],B[N];
		reget(dega+degb-1);copy(a,a+dega,A);copy(b,b+degb,B);
		ntt(A,m,1);ntt(B,m,1);
		for(int i=0;i<m;++i) A[i]=mul(A[i],B[i]);
		ntt(A,m,-1);
		copy(A,A+dega+degb-1,c);fill(c+dega+degb-1,c+m,0);fill(A,A+m,0);fill(B,B+m,0);
	}
}
using namespace Math;

namespace DreamLolita
{
	int n;ll m;
	int p[N],f[N],g[N],h[N];
	void solution()
	{
		initmath();n=read();m=read();
		for(int i=0;i<=n;++i) p[i]=read();
		for(int i=0;i<=n;++i) h[i]=ifac[i],f[i]=mul(p[i],fac[i]);
		reverse(h,h+n+1);
		polymult(h,f,g,n+1,n+1);
		for(int i=0;i<=n;++i) g[i]=mul(g[i+n],ifac[i]),f[i]=0;
		for(int i=n+1;i<=n*2;++i) g[i]=0,f[i]=0;
		for(int i=0;i<=n;++i) g[i]=mul(mul(g[i],(int)qpow(inv[i+1],m)),fac[i]);
		for(int i=0;i<=n;++i) h[i]=mul(h[i],(n-i)&1?mod-1:1);
		polymult(g,h,f,n+1,n+1);
		for(int i=0;i<=n;++i) f[i]=mul(f[i+n],ifac[i]);
		for(int i=0;i<=n;++i) writesp(f[i]);
	}
}

int main()
{
#ifdef Durant_Lee
	freopen("CF947E.in","r",stdin);
	freopen("CF947E.out","w",stdout);
#endif
	DreamLolita::solution();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值