• WD与积木


    (T)组询问,每次询问(n)个有标号的球随机放入任意个有标号的集合中,不能有空集的集合数量的期望。(n,Tleq 10^5)


    期望转计数,答案就是所有情况的集合数之和/情况数。设(g_n)表示情况数,用有标号计数的经典转移:枚举第一个集合中有哪些球,得到

    [egin{align} g_n=sum_{i=1}^n{nchoose i}g_{n-i}=sum_{i=0}^n{nchoose i}g_{n-i}-g_n \2g_n=sum_{i=0}^n{nchoose i}g_{n-i}(ngeq 1) \g_0=1 end{align} ]

    根据(g)的转移很显然能写出(g)(EGF)

    [egin{align} 2G(x)=e^xG(x)+1 \G(x)=frac{1}{2-e^x} end{align} ]

    多项式求逆就可以求出(G)了。

    接下来设(f_n)表示所有情况的集合数之和。转移类似地有:

    [egin{align} \f_n=sum_{i=1}^n{nchoose i}(g_{n-i}+f_{n-i})=g_n+sum_{i=1}^n{nchoose i}f_{n-i}=g_n+sum_{i=0}^n{nchoose i}f_{n-i}-f_n \2f_n=g_n+sum_{i=0}^n{nchoose i}f_{n-i}(ngeq 1) \f_0=0 end{align} ]

    还是写出(f)(EGF)

    [egin{align} \2F(x)=G(x)+e^xF(x)-1 \(e^x-2)F(x)=1-G(x)=frac{1-e^x}{2-e^x} \F(x)=frac{e^x-1}{(e^x-2)^2} end{align} ]

    仍然多项式求逆,最后(n)的答案就是(frac{[x^n]F(x)}{[x^n]G(x)})。复杂度(mathcal{O}(nlog n))

    #include<bits/stdc++.h>
    #define rg register
    #define il inline
    #define cn const
    #define gc getchar()
    #define fp(i,a,b) for(rg int i=(a),ed=(b);i<=ed;++i)
    #define fb(i,a,b) for(rg int i=(a),ed=(b);i>=ed;--i)
    #define go(u) for(rg int i=head[u];~i;i=e[i].nxt)
    using namespace std;
    typedef cn int cint;
    typedef long long LL;
    il void rd(int &x){
        x = 0;
    	rg int f(1); rg char c(gc);
    	while(c<'0'||'9'<c){if(c=='-')f=-1;c=gc;}
    	while('0'<=c&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
    	x*=f;
    }
    cint maxn = 100010, mod = 998244353, invG = (mod+1)/3;
    int T, n, a[maxn], fac[maxn], ifac[maxn];
    int A[maxn<<2], F[maxn<<2], G[maxn<<2], inv_ary[maxn<<2];
    int lim, hst, rev[maxn<<2];
    il int fpow(int a, int b, int ans = 1){
    	for(; b; b >>= 1, a = 1ll*a*a%mod)if(b&1)ans = 1ll*ans*a%mod;
    	return ans;
    }
    il void ntt(int *a, cint &typ){
    	fp(i, 0, lim-1)if(i > rev[i])swap(a[i], a[rev[i]]);
    	for(rg int md = 1; md < lim; md <<= 1){
    		rg int len = md<<1, Gn = fpow(typ ? invG : 3, (mod-1)/len);
    		for(rg int l = 0; l < lim; l += len){
    			for(rg int nw = 0, Pow = 1; nw < md; ++nw, Pow = 1ll*Pow*Gn%mod){
    				rg int x = a[l+nw], y = 1ll*a[l+nw+md]*Pow%mod;
    				a[l+nw] = (x+y)%mod, a[l+nw+md] = (x-y+mod)%mod;
    			}
    		}
    	}
    	if(!typ)return;
    	rg int inv = fpow(lim, mod-2);
    	fp(i, 0, lim-1)a[i] = 1ll*a[i]*inv%mod;
    }
    il void init(int n){
    	lim = 1, hst = 0;
    	while(lim < n)lim <<= 1, ++hst;
    	fp(i, 1, lim-1)rev[i] = (rev[i>>1]>>1)|((i&1)<<hst-1);
    }
    void get_inv(int *a, int *f, int n){
    	if(n == 1)return f[0] = fpow(a[0], mod-2), void();
    	get_inv(a, f, n+1>>1), init(n*2-1);
    	fp(i, 0, n-1)inv_ary[i] = a[i];
    	ntt(f, 0), ntt(inv_ary, 0);
    	fp(i, 0, lim-1)f[i] = 1ll*f[i]*(2-1ll*f[i]*inv_ary[i]%mod+mod)%mod;
    	ntt(f, 1);
    	fp(i, n, lim-1)f[i] = 0;
    	fp(i, 0, lim-1)inv_ary[i] = 0;
    }
    int main(){
    	rd(T);
    	fp(i, 1, T)rd(a[i]), n = max(n, a[i]);
    	fac[0] = 1; fp(i, 1, n)fac[i] = 1ll*fac[i-1]*i%mod;
    	ifac[n] = fpow(fac[n], mod-2); fb(i, n, 1)ifac[i-1] = 1ll*ifac[i]*i%mod;
    	
    	fp(i, 0, n)A[i] = mod-ifac[i];
    	A[0] = (A[0]+2)%mod, get_inv(A, G, n+1);
    	
    	init(n*2+1), ntt(A, 0);
    	fp(i, 0, lim-1)A[i] = 1ll*A[i]*A[i]%mod;
    	ntt(A, 1);
    	fp(i, n+1, lim-1)A[i] = 0;
    	get_inv(A, F, n+1);
    	fp(i, 1, n)A[i] = ifac[i];
    	A[0] = 0, init(n*2+1), ntt(A, 0), ntt(F, 0);
    	fp(i, 0, lim-1)F[i] = 1ll*F[i]*A[i]%mod;
    	ntt(F, 1);
    	
    	fp(i, 1, T)printf("%lld
    ", 1ll*F[a[i]]*fpow(G[a[i]], mod-2)%mod);
    	return 0;
    }
    
  • 相关阅读:
    PSP ISO游戏运行必备工具:ISO TOOL 1.970 功能一览&图文教程
    Linux防火墙(原书第3版) 电子书籍
    iptables的相关概念和数据包的流程(图)
    oracle数据库远程连接服务器配置tnsnames
    编程感悟
    工作任务三 打印表单数据
    UltraWebTree的使用心得
    DropDownList应用
    使用UltraWebTree时,如何在刷新后展开之前选中的节点,并绑定相关数据
    webgrid 添加行是不允许相同
  • 原文地址:https://www.cnblogs.com/akura/p/14494442.html
Copyright © 2020-2023  润新知