zoukankan      html  css  js  c++  java
  • 「CTS2019 | CTSC2019」珍珠

    「CTS2019 | CTSC2019」珍珠

    题目描述:

    (n) 个在范围 [1,D]内的整数均匀随机变量。
    求至少能选出 (m) 个瓶子,使得存在一种方案,选择一些变量,并把选出来的每一个变量放到一个瓶子中,满足每个瓶子都恰好装两个值相同的变量的概率。
    请输出概率乘上 (D^n)后对 998244353取模的值.

    思路

    答案只与最终奇数的个数有关,偶数个都是可以直接匹配的。设剩下(t)个奇数,那么能匹配出的数量就是((n-t)/2) 如果((n-D)/2>=m)那就是可以随便放,(n/2<m)说明无解。

    1-12

    (dp[i][j]) 表示取到了第(i)个数,此时没有配对的个数为(j)的概率,那么能配对的个数就是((n-j)/2)

    就是方案数

    12-15

    构造生成函数。

    因为最终的结果只和奇数的个数有关,假设(G_i)表示恰好有(i)个奇数,对于恰好的问题可以考虑改成容斥,所以用(f_k)表示至少有(k)个奇数的方案数。

    前置知识:

    泰勒展开:(e^x=sum_{i=0}^{∞}frac{x^i}{i!}) (可以当做是随便取)

    关于奇数的指数型函数:(frac{e^x-e^{-x}}{2}=sum_{i=0}^{∞}frac{x^{2i+1}}{(2i+1)!}) (放进去表示只去奇数)

    (f_k)表示至少有(k)个奇数

    (f_k=inom{D}{k}[x^n]*n!*(frac{e^x-e^{-x}}{2})^k*(e^x)^{D-k})

    (f_k=inom{D}{k}[x^n]*frac{n!}{2^k}*(e^x-e^{-x})^k*(e^x)^{D-k})

    ((e^x-e^{-x})^k)二项式展开和后面的进行合并

    (f_k=inom{D}{k}frac{n!}{2^k}*sum_{j=0}^{k}inom{k}{j}(-1)^{k-j}*e^{(D-2*(k-j))x}[x^n])

    (e^{ax}=sum_{i=0}^{∞}frac{(ax)^i}{i!}=frac{a^ix^i}{i!})

    所以第(n)位的系数就是(frac{a^i}{i!})

    (f_k=inom{D}{k}frac{n!}{2^k}*sum_{j=0}^{k}inom{k}{j}(-1)^{k-j}*frac{(D-2*(k-j))^n}{n!})

    (f_k=frac{D!}{(D-k)!*2^k}sum_{j=0}^{k}frac{(-1)^{k-j}}{(k-j)!*(j!)}*(D-2k-2j)^n)

    (k-j=j)(即改成枚举k-j) 就变成了

    (f_k=frac{D!}{(D-k)!*2^k}sum_{j=0}^{k}frac{(-1)^j}{(k-j)!*(j!)}*(D-2j)^n)

    (f_k=frac{D!}{(D-k)!*2^k}sum_{j=0}^{k}frac{(-1)^j*(D-2j)^n}{(j!)}*frac{1}{(k-j)!})

    然后后半部分可以进行卷积

    (g=sum_{j=0}^{k}frac{(-1)^j*(D-2j)^n}{(j!)})

    (t=sum_{j=0}^{k}frac{1}{j!})

    (p=g*t)

    (f_k=frac{D!}{(D-k)!*2^k}*p(k))

    设恰好有(k)个奇数为G

    (G_i=sum_{j}inom{j}{i}*(-1)^{j-i}*f_j)

    (G_i=frac{1}{i!}sum_j j!*f_j*frac{(-1)^{j-i}}{(j-i)!})

    然后再进行一次卷积

    右边要变成(i-j)的形式,-(i-j)是正数

    (G_i=frac{1}{i!}sum_jj!*f_j*frac{(-1)^{i-j}}{[-(i-j)]!})

    #include<bits/stdc++.h>
    #define ll long long
    #define M 100005
    using namespace std;
    const int Mod=998244353,w0=3,w1=332748118;
    void Rd(int &res) {
    	res=0;
    	char c;
    	int fl=1;
    	while(c=getchar(),c<48)if(c=='-')fl=-1;
    	do res=(res<<1)+(res<<3)+(c^48);
    	while(c=getchar(),c>=48);
    	res*=fl;
    }
    int D,n,m,R[M<<2];
    void add(ll &x,ll y) {
    	x+=y;
    	if(x>=Mod)x-=Mod;
    	if(x<0)x+=Mod;
    }
    ll mul(ll x,ll y) {
    	ll res=1;
    	x=(x%Mod+Mod)%Mod;
    	while(y) {
    		if(y&1)res=res*x%Mod;
    		x=x*x%Mod,y>>=1;
    	}
    	return res;
    }
    void NTT(ll *a,int n,int op) {
    	for(int i=0; i<n; i++)if(i<R[i])swap(a[i],a[R[i]]);
    	for(int i=2; i<=n; i<<=1) {
    		int w=op?w1:w0;
    		w=mul(w,((Mod-1)/i));//就是原根的n次方
    		for(int j=0; j<n; j+=i) {
    			int l=i/2,res=1;
    			for(int k=0; k<l; k++) {
    				int t=1ll*res*a[j+k+l]%Mod;
    				a[j+k+l]=(a[j+k]-t+Mod)%Mod,a[j+k]=(a[j+k]+t)%Mod;
    				res=1ll*res*w%Mod;
    			}
    		}
    	}
    	if(op) {
    		ll res=mul(n,Mod-2);
    		for(int i=0; i<n; i++)a[i]=a[i]*res%Mod;
    	}
    }
    ll G[M<<2],T[M<<2],P[M<<2],pr[M],inv[M],F[M<<2];
    int main() {
    	Rd(D),Rd(n),Rd(m);
    	if((n-D)>=2ll*m)printf("%lld
    ",mul(D,n));
    	else if(n<2ll*m)puts("0");
    	else {
    		pr[0]=inv[0]=1;
    		for(int i=1; i<=D; i++)pr[i]=pr[i-1]*i%Mod;
    		inv[D]=mul(pr[D],Mod-2);
    		for(int i=D; i; i--)inv[i-1]=inv[i]*i%Mod;
    		int res=1,l=0;
    		for(int i=0; i<=D; i++)G[i]=res*mul(D-2*i,n)%Mod*inv[i]%Mod,res*=-1,G[i]=(G[i]+Mod)%Mod;
    		for(int i=0; i<=D; i++)T[i]=inv[i];
    		l=1,res=0;
    		while(l<=D+D)l<<=1,res++;
    		for(int i=D+1; i<l; i++)G[i]=T[i]=0;
    		for(int i=0; i<l; i++)R[i]=(R[i>>1]>>1)|((i&1)<<(res-1));
    		NTT(G,l,0),NTT(T,l,0);
    		for(int i=0; i<l; i++)G[i]=G[i]*T[i]%Mod;
    		NTT(G,l,1);
    		res=1;
    		for(int i=0; i<=D; i++)F[i]=pr[D]*inv[D-i]%Mod*res%Mod*G[i]%Mod,res=inv[2]*res%Mod;
    		for(int i=D+1; i<l; i++)F[i]=T[i]=0;
    		res=1;
    		for(int i=0; i<=D; i++)F[i]=F[i]*pr[i]%Mod;
    		for(int i=0; i<=D; i++)T[D-i]=res*inv[i]%Mod,add(T[D-i],Mod),res*=-1;
    		NTT(F,l,0),NTT(T,l,0);
    		for(int i=0; i<l; i++)F[i]=F[i]*T[i]%Mod;
    		NTT(F,l,1);
    		ll ans=0;
    		for(int i=0; i<=D; i++)if((n-i)/2>=m)add(ans,F[D+i]*inv[i]%Mod);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    软件测试基础
    Python
    Python
    C# 打开帮助文档,打开电脑中其他应用或者文件
    C# 设置窗口大小为不可调、取消最大化、最小化窗口按键
    C# 控件置于最顶层、最底层、隐藏、显示
    C# 在窗口绘制图形(打点、画圆、画线)
    C# 不同窗口传递参数
    C# 禁止在textBox输入框输入非法字符
    C# 设定弹出窗体位置
  • 原文地址:https://www.cnblogs.com/cly1231/p/12935687.html
Copyright © 2011-2022 走看看