Codeforces923E Perpetual Subtraction

Description

  • 黑板上有一个整数 x,初始时它的值为 [0,n] 中的某一个整数,其中为 i 的概率是 pi
  • 每一轮你可以将 x 完全随机地替换成 [0,x] 中的一个数。
  • m 轮后,对于每个 i[0,n]x=i 的概率。
  • n105m1018,答案对 998244353 取模。

Solution

fz,i 表示经过 z 轮得到数字 i 的概率,显然有 f0,i=pi,fz,i=j=infz1,j1j+1

使用 OGF 来刻画这个过程,那么写出来 Fz(x) 看一看

Fz(x)=i=1nfz,ixi=i=1n(j=infz1,j1j+1)xi=j=1nfz1,j1j+11xj+11x

省略了一步等比数列求和,问题不大

注意到 xj+1j+1=0xkj dk,1j+1=01kj dk 两者相减就是改变积分下界

Fz(x)=1x1j=1nfz1,j1xkj dk

代换 Gz(x)=Fz(x+1) 来避免 1x1 和非零下界,那么:

Gz(x)=1x1x+1j=1nfz1,jkj dk=1x0xFz1(k+1) dk=1x0xGz1(k) dk[xi]Gz(x)=1i+1[xi]Gz1(x)

那么如果得到了 G0 可以快速得到 Gm,使用二项式定理和二项式反演即可得到 F0G0 以及 GmFm 的变换,具体而言:

G0(x)=i=1nf0,i(x+1)i=i=1nf0,ij=0i(ij)xj=i=0nxij=iif0,j(ji)

Fm(x)=i=0nxij=in(1)ji(ji)gm,j

全部是减法卷积

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
const int mod=998244353;
inline int add(int x,int y,int Mod=mod){return x+y>=Mod?x+y-Mod:x+y;}
inline int del(int x,int y,int Mod=mod){return x-y<0?x-y+Mod:x-y;}
inline int mul(int x,int y,int Mod=mod){return x*y-x*y/Mod*Mod;}
inline void ckadd(int &x,int y,int Mod=mod){x=x+y>=Mod?x+y-Mod:x+y;}
inline void ckdel(int &x,int y,int Mod=mod){x=x-y<0?x-y+Mod:x-y;}
inline void ckmul(int &x,int y,int Mod=mod){x=x*y-x*y/Mod*Mod;}
inline int ksm(int x,int y,int Mod=mod){int res=1; for(;y;y>>=1,ckmul(x,x,Mod)) if(y&1) ckmul(res,x,Mod); return res;}
inline void approx(int val,int Mod=mod,int lim=1e5){int x=val,y=Mod,a=1,b=0; while(x>lim){swap(x,y); swap(a,b); a-=x/y*b; x%=y;} cout<<x<<"/"<<a<<endl; return ;}
const int N=5e5+10;
#define poly vector<int> 
int r[N],W[N];
int fac[N],ifac[N],inv[N],p[N],n,m;
inline void NTT(vector<int> &f,int lim,int opt){
	f.resize(lim);
	for(int i=0;i<lim;++i){
		r[i]=r[i>>1]>>1|((i&1)?(lim>>1):0);
		if(i<r[i]) swap(f[i],f[r[i]]);
	}
	for(int p=2;p<=lim;p<<=1){
		int len=p>>1; W[0]=1; W[1]=ksm(3,(mod-1)/p);
		if(opt==-1) W[1]=ksm(W[1],mod-2);
		for(int j=2;j<len;++j) W[j]=mul(W[j-1],W[1]);
		for(int k=0;k<lim;k+=p){
			for(int l=k;l<k+len;++l){
				int tt=mul(f[l+len],W[l-k]);
				f[l+len]=del(f[l],tt);
				ckadd(f[l],tt);
			}
		}
	}
	if(opt==-1) for(int i=0,tmp=ksm(lim,mod-2);i<lim;++i) ckmul(f[i],tmp);
	return ;
}
inline poly Mul(poly a,poly b){
	int n=a.size(),m=b.size(),lim=1;
	while(lim<=(n+m)) lim<<=1;
	NTT(a,lim,1); NTT(b,lim,1);
	for(int i=0;i<lim;++i) ckmul(a[i],b[i]);
	NTT(a,lim,-1);
	a.resize(n+m-1);
	return a;
}
signed main(){
	n=5e5; fac[0]=inv[0]=1;
	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],mod-2);
	for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
	scanf("%lld%lld",&n,&m);
	for(int i=0;i<=n;++i) scanf("%lld",&p[i]);
	poly A(n+1),B(n+1),G(n+1);
	for(int i=0;i<=n;i++) A[i]=ifac[i],B[n-i]=mul(fac[i],p[i]);
	poly C=Mul(A,B);
	for(int i=0;i<=n;i++) G[i]=mul(ksm(inv[i+1],m),mul(ifac[i],C[n-i]));
	A.clear(); B.clear();
	A.resize(n+1); B.resize(n+1);
	for(int i=0;i<=n;i++){
		if(!(i&1)) A[i]=ifac[i];
		else A[i]=del(0,ifac[i]);
		B[n-i]=mul(fac[i],G[i]);
	} 
	C=Mul(A,B);
	for(int i=0;i<=n;i++) printf("%lld ",mul(C[n-i],ifac[i]));
	putchar('\n');
	return 0;
}

本题另有知识门槛比较高的相似对角化相关做法,比较关键的一点是注意到上三角矩阵的特征值就是对角线上元素

posted @   没学完四大礼包不改名  阅读(48)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示