• LG4705 玩游戏


    玩游戏

    Alice 和 Bob 又在玩游戏。 对于一次游戏,首先 Alice 获得一个长度为 (n) 的序列 (a),Bob 获得一个长度为 (m) 的序列 (b)。之后他们各从自己的序列里随机取出一个数,分别设为 (a_x, b_y),定义这次游戏的 (k) 次价值为 ((a_x + b_y)^k)。 由于他们发现这个游戏实在是太无聊了,所以想让你帮忙计算对于 (i = 1, 2, cdots, t),一次游戏 (i) 次价值的期望是多少。 由于答案可能很大,只需要求出模 (998244353) 下的结果即可。

    (1 leq n, m leq 10^5,1 leq t leq 10^5)

    题解

    https://www.luogu.com.cn/blog/user48173/solution-p4705

    一次游戏的期望 (k) 次价值为

    [ans_k=frac{1}{nm}sum_{i=1}^nsum_{j=1}^m(a_i+b_j)^k\ nmcdot ans=sum_{i=1}^nsum_{j=1}^msum_{r=0}^kinom{k}{r}a_i^rb_j^{k-r}\ =k!sum_{r=0}^kfrac{sum_{i=1}^na_i^r}{r!}frac{sum_{j=1}^mb_j^{k-r}}{(k-r)!} ]

    显然问题转化成了求等指数幂和。

    [sum_{i=1}^nfrac{1}{1-a_ix}=frac{sum_{i=1}^nprod_{j eq i}(1-a_jx)}{prod_{i=1}^n(1-a_ix)} ]

    分母可以分治NTT求。分子的 ([x^i]) 等于分母的 ([x^i] imes (n-i))

    时间复杂度 (O(nlog^2 n+tlog t))

    CO int N=262144;
    int omg[2][N],rev[N];
    int fac[N],inv[N],ifac[N];
    
    void NTT(poly&a,int dir){
    	int lim=a.size(),len=log2(lim);
    	for(int i=0;i<lim;++i) rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
    	for(int i=0;i<lim;++i)if(i<rev[i]) swap(a[i],a[rev[i]]);
    	for(int i=1;i<lim;i<<=1)
    		for(int j=0;j<lim;j+=i<<1)for(int k=0;k<i;++k){
    			int t=mul(omg[dir][N/(i<<1)*k],a[j+i+k]);
    			a[j+i+k]=add(a[j+k],mod-t),a[j+k]=add(a[j+k],t);
    		}
    	if(dir==1){
    		int ilim=fpow(lim,mod-2);
    		for(int i=0;i<lim;++i) a[i]=mul(a[i],ilim);
    	}
    }
    poly operator~(poly a){
    	int n=a.size();
    	poly b={fpow(a[0],mod-2)};
    	if(n==1) return b;
    	a.resize(1<<(int)ceil(log2(n)));
    	for(int lim=2;lim<2*n;lim<<=1){
    		poly c(a.begin(),a.begin()+lim);
    		c.resize(lim<<1),NTT(c,0);
    		b.resize(lim<<1),NTT(b,0);
    		for(int i=0;i<lim<<1;++i) b[i]=mul(2+mod-mul(c[i],b[i]),b[i]);
    		NTT(b,1),b.resize(lim);
    	}
    	return b.resize(n),b;
    }
    poly operator*(poly a,poly b){
    	int n=a.size()+b.size()-1;
    	int lim=1<<(int)ceil(log2(n));
    	a.resize(lim),NTT(a,0);
    	b.resize(lim),NTT(b,0);
    	for(int i=0;i<lim;++i) a[i]=mul(a[i],b[i]);
    	NTT(a,1),a.resize(n);
    	return a;
    }
    
    int a[N],b[N];
    
    poly solve(int a[],int l,int r){
    	if(l==r) return poly{1,mod-a[l]};
    	int mid=(l+r)>>1;
    	return solve(a,l,mid)*solve(a,mid+1,r);
    }
    int main(){
    	omg[0][0]=1,omg[0][1]=fpow(3,(mod-1)/N);
    	omg[1][0]=1,omg[1][1]=fpow(omg[0][1],mod-2);
    	fac[0]=fac[1]=1;
    	inv[0]=inv[1]=1;
    	ifac[0]=ifac[1]=1;
    	for(int i=2;i<N;++i){
    		omg[0][i]=mul(omg[0][i-1],omg[0][1]);
    		omg[1][i]=mul(omg[1][i-1],omg[1][1]);
    		fac[i]=mul(fac[i-1],i);
    		inv[i]=mul(mod-mod/i,inv[mod%i]);
    		ifac[i]=mul(ifac[i-1],inv[i]);
    	}
    	int n=read<int>(),m=read<int>();
    	for(int i=1;i<=n;++i) read(a[i]);
    	for(int i=1;i<=m;++i) read(b[i]);
    	int t=read<int>();
    	poly da=solve(a,1,n),ua(n);
    	poly db=solve(b,1,m),ub(m);
    	for(int i=0;i<n;++i) ua[i]=mul(da[i],n-i);
    	for(int i=0;i<m;++i) ub[i]=mul(db[i],m-i);
    	da.resize(t+1),db.resize(t+1);
    	ua=ua*~da,ua.resize(t+1);
    	ub=ub*~db,ub.resize(t+1);
    	for(int i=0;i<=t;++i){
    		ua[i]=mul(ua[i],ifac[i]);
    		ub[i]=mul(ub[i],ifac[i]);
    	}
    	ua=ua*ub,ua.resize(t+1);
    	int inm=fpow(mul(n,m),mod-2);
    	for(int i=1;i<=t;++i){
    		int ans=mul(ua[i],mul(fac[i],inm));
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    PAT 1008--------数组元素的循环右移,你需要记住的
    PAT1049-----枚举法,找规律题,注意降低时间复杂度
    PAT1048----你需要了解并记住的解题思路
    C++中几个输入函数的用法和区别(cin、cin.get()、cin.getline()、getline()、gets()、getchar()))
    PAT1040----关于数学题目的解法新思路值得借鉴,字符的配对
    PAT1029-----介绍字符串的解题思路和部分知识点
    PAT1027-----等差数列的问题或数学问题
    PAT1026----四舍五入的思路,%2d的一些知识
    / 已阅 /PAT1017-------高精度计算,问题的所有可能情况
    LeetCode 无重复字符的最长子串
  • 原文地址:https://www.cnblogs.com/autoint/p/12187793.html
Copyright © 2020-2023  润新知