洛谷P2791 幼儿园篮球题

题目描述
题解

可以列出式子 ∑ i = 0 k ( i m ) ( k − i n − m ) i L ( k n ) \frac{\sum_{i=0}^k(_i^m)(_{k-i}^{n-m})i^L}{(_k^n)} (kn)i=0k(im)(kinm)iL
我们只关心上面那部分,套路把 i L i^L iL 换掉,得 ∑ i = 0 k ( i m ) ( k − i n − m ) ∑ j = 0 L { j L } ( j i ) j ! \sum_{i=0}^k(_i^m)(_{k-i}^{n-m})\sum_{j=0}^L\{_j^L\}(_j^i)j! i=0k(im)(kinm)j=0L{jL}(ji)j!
j j j 提前,得 ∑ j = 0 L { j L } j ! ∑ i = 0 k ( i m ) ( k − i n − m ) ( j i ) \sum_{j=0}^L\{_j^L\}j!\sum_{i=0}^k(_i^m)(_{k-i}^{n-m})(_j^i) j=0L{jL}j!i=0k(im)(kinm)(ji)
由于 ( i m ) ( j i ) = ( j m ) ( i − j m − j ) (_i^m)(_j^i)=(_j^m)(_{i-j}^{m-j}) (im)(ji)=(jm)(ijmj) ,于是我们接着化 ∑ j = 0 L { j L } j ! ( j m ) ∑ i = 0 k ( i − j m − j ) ( k − i n − m ) \sum_{j=0}^L\{_j^L\}j!(_j^m)\sum_{i=0}^k(_{i-j}^{m-j})(_{k-i}^{n-m}) j=0L{jL}j!(jm)i=0k(ijmj)(kinm)
我们观察到后面的式子,考虑其意义,就是在 m − j m-j mj 个中选 i − j i-j ij 个数乘上在 n − m n-m nm 个数中选出 k − i k-i ki 个数的总方案数,由于 i i i 是从 0 0 0 k k k ,所以可以看成是在 n − j n-j nj 个数中选出 k − j k-j kj 个数,于是我们得到式子 ∑ j = 0 L { j L } j ! ( j m ) ( k − j n − j ) \sum_{j=0}^L\{_j^L\}j!(_j^m)(_{k-j}^{n-j}) j=0L{jL}j!(jm)(kjnj)
于是我们可以预处理 { j L } \{_j^L\} {jL} 即可

代码
#include <bits/stdc++.h>
using namespace std;
const int N=8e5+5,M=2e7+5,P=998244353;
int n,m,T,L,G[2]={3,(P+1)/3},A[N],B[N],jc[M],ny[M],t=1,p,r[N];
int X(int x){return x>=P?x-P:x;}
int K(int x,int y){
	int z=1;
	for (;y;y>>=1,x=1ll*x*x%P)
		if (y&1) z=1ll*z*x%P;
	return z;
}
void Ntt(int *g,bool o){
	for (int i=0;i<t;i++)
		if (i<r[i]) swap(g[i],g[r[i]]);
	for (int wn,i=1;i<t;i<<=1){
		wn=K(G[o],(P-1)/(i<<1));
		for (int x,y,j=0;j<t;j+=(i<<1))
			for (int w=1,k=0;k<i;k++,w=1ll*w*wn%P)
				x=g[j+k],y=1ll*w*g[i+j+k]%P,
				g[j+k]=X(x+y),g[i+j+k]=X(x-y+P);
	}
	if (o)
		for (int i=0,v=K(t,P-2);i<t;i++)
			g[i]=1ll*v*g[i]%P;
};
int main(){
	cin>>n>>m>>T>>L;
	n=max(n,L);jc[0]=1;
	for (int i=1;i<=n;i++)
		jc[i]=1ll*i*jc[i-1]%P;
	ny[n]=K(jc[n],P-2);
	for (int i=n;i;i--)
		ny[i-1]=1ll*i*ny[i]%P;
	for (int i=0,F=1;i<=L;i++,F=P-F)
		A[i]=1ll*F*ny[i]%P,
		B[i]=1ll*K(i,L)*ny[i]%P;
	for (;t<L+L+2;t<<=1,p++);
	for (int i=0;i<t;i++)
		r[i]=(r[i>>1]>>1)|((i&1)<<(p-1));
	Ntt(A,0);Ntt(B,0);
	for (int i=0;i<t;i++)
		A[i]=1ll*A[i]*B[i]%P;
	Ntt(A,1);
	for (int x,k,v;T--;){
		scanf("%d%d%d",&n,&m,&k);
		v=min(min(n,m),min(k,L));x=0;
		for (int i=0;i<=v;i++)
			x=X(x+1ll*A[i]*ny[m-i]%P*jc[n-i]%P*ny[k-i]%P);
		x=1ll*x*jc[m]%P*ny[n]%P*jc[k]%P;
		printf("%d\n",x);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值