2020杭电多校第五场1012-Set1

题目大意

分析

求概率可以转化成计数,那么对于第i个位置,就是求有多少种方案数不会选到i,除以总方案数即可,总方案数很简单,就是(n-1)!!。
假如我们把i剔除掉,断开的区间合并起来形成一个长度为n-1区间,那么这个区间有多少种合法方案数?显然是(n-2)!!。但真的有那么多方案不会选到i吗?
考虑不会选到i的方案数长什么样子:设每次选取的两个数为(x,y),那么我们要保证所有的x都小于i。而很显然(n-2)!!里包含了x大于i的方案。
观察到当i<n/2的时候,方案数肯定为0,而i>=n/2时,我们要保证方案的x均位于1~i-1。那么等价于保证i+1 ~n均对应某个y。现在我们只需要求,i+1 ~n的位置均对应y的方案数即可。设req=n-i,方案数为
C i − 1 r e q ∗ r e q !   ∗ ( i − 1 − r e q    − 1 ) ! ! C_{i-1}^{req}*req!\ *(i-1-req \ \ -1)!! Ci1reqreq! (i1req  1)!!
前面两个因子表示,我要为后面的y,在1~i-1中要选req个对应的x,然后顺序可以乱安排。第三项表示前面剩下未确定的位置的总方案数:前面未确定的还剩i-1-req个,对应的合法方案数就是 (size-1)!!

代码

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
typedef double db;
const int N=5e6+5,M=1e6+5,mo=998244353;
ll irev[N],rev[N],rev2[N],f[N],ans,fac[N];
int T,n,i,j,req;
void irev_predo(int n,int p)
{
	int i;
	irev[1]=1;
	fo(i,2,n)
	{
		irev[i]=-(ll)(p/i)*irev[p%i]%p;
		if (irev[i]<0) irev[i]+=p; 
	}
}
void predo(int n)
{
	fac[0]=1;
	fo(i,1,n) fac[i]=fac[i-1]*i%mo;
	rev[1]=rev[0]=1;
	rev2[1]=rev2[0]=1;
	fo(i,2,n) rev[i]=rev[i-1]*irev[i]%mo,rev2[i]=rev2[i-2]*irev[i]%mo;
	f[0]=1;
	fo(i,0,n/2)
		f[i+1]=f[i]*(2*i+1)%mo*(i+1)%mo;
	fo(i,0,n/2) f[i]=f[i]*rev[i]%mo;
}
ll c(int n,int m)
{
	if (n>m||m<0||n<0) return 0;
	if (n==m) return 1;
    return 1ll*fac[m]*rev[n]%mo*rev[m-n]%mo;
}
void Print(ll x)
{
	if (!x) putchar('0');
	int w[20];
	w[0]=0;
	while (x) w[++w[0]]=x%10,x/=10;
	while (w[0]) putchar('0'+w[w[0]--]);
}	
int main()
{
    freopen("12.in","r",stdin);
	freopen("12.out","w",stdout);
	n=5e6;
	irev_predo(n,mo);
	predo(n); 
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		fo(i,1,n)
		{
			req=n-i;
			if (i-1<req) ans=0;
			else
			{
				//c(req,i-1)*fac[req]%mo*f[]
				ans=c(req,i-1)*fac[req]%mo*f[(i-1-req)/2]%mo*rev2[n-1]%mo;
			}	
			Print(ans);
			if (i!=n) putchar(' ');
		}
		putchar('\n');
	}
	
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值