Luogu 2791 幼儿园篮球题

发布时间 2023-07-20 21:32:58作者: Ender_32k

考虑枚举选出来 \(i\)没气的篮球,那么答案可以表示成:

\[\text{ans}=\frac{1}{\dbinom{n}{k}}\sum\limits_{i=0}^{k}\dbinom{m}{i}\dbinom{n-m}{k-i}i^L \]

注意到这里的组合数 \(\dbinom{n}{m}\)\(n<m\) 或者 \(m<0\) 时无意义,直接当成 \(0\) 即可。

考虑普通幂转下降幂:

\[i^L=\sum\limits_{j=0}^L\begin{Bmatrix}L\\j\end{Bmatrix}i^{\underline j} \]

带入原式有:

\[\begin{aligned}\text{ans}&=\frac{1}{\dbinom{m}{k}}\sum\limits_{i=0}^k\dbinom{m}{i}\dbinom{n-m}{k-i}\sum\limits_{j=0}^L\begin{Bmatrix}L\\j\end{Bmatrix}i^{\underline j}\\&=\frac{1}{\dbinom{m}{k}}\sum\limits_{j=0}^L\begin{Bmatrix}L\\j\end{Bmatrix}\sum\limits_{i=0}^k\dbinom{m}{i}\dbinom{n-m}{k-i}i^{\underline j}\end{aligned} \]

根据经典组合恒等式:

\[\begin{aligned}\dbinom{m}{i}i^{\underline j}&=\frac{m!}{i!(m-i)!}\cdot \frac{i!}{(i-j)!}\\&=\frac{m!}{(m-i)!(i-j)!}\\&=\frac{(m-j)!}{(m-i)!(i-j)!}\cdot \frac{m!}{(m-j)!}\\&=\dbinom{m-j}{i-j}m^{\underline j}\end{aligned} \]

代入原式得:

\[\begin{aligned}\text{ans}&=\frac{1}{\dbinom{m}{k}}\sum\limits_{j=0}^L\begin{Bmatrix}L\\j\end{Bmatrix}\sum\limits_{i=0}^k\dbinom{n-m}{k-i}\dbinom{m-j}{i-j}m^{\underline j}\\&=\frac{1}{\dbinom{m}{k}}\sum\limits_{j=0}^L\begin{Bmatrix}L\\j\end{Bmatrix}m^{\underline j}\sum\limits_{i=0}^k\dbinom{n-m}{k-i}\dbinom{m-j}{i-j}\\&=\frac{1}{\dbinom{m}{k}}\sum\limits_{j=0}^L\begin{Bmatrix}L\\j\end{Bmatrix}m^{\underline j}\dbinom{n-j}{k-j}\end{aligned} \]

第三步运用了范德蒙德卷积

于是我们只需要求出第 \(L\) 行的斯特林数值,就可以通过 \(O(N)\) 预处理阶乘及其逆元,对于每个询问 \(O(L)\) 得出答案。

这是个经典 trick,通过二项式反演以及 NTT 可以 \(O(L\log L)\)求出,可以看 P5395 第二类斯特林数·行,这里不多赘述。

总复杂度 \(O(N+L(\log L+q))\)

// Problem: P2791 幼儿园篮球题
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2791
// Memory Limit: 222 MB
// Time Limit: 1110 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
using namespace std;

namespace vbzIO {
    char ibuf[(1 << 20) + 1], *iS, *iT;
    #if ONLINE_JUDGE
    #define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
    #else
    #define gh() getchar()
    #endif
    #define mt make_tuple
    #define mp make_pair
    #define fi first
    #define se second
    #define pc putchar
    #define pb emplace_back
    #define ins insert
    #define era erase
    typedef tuple<int, int, int> tu3;
    typedef pair<int, int> pi;
    inline int rd() {
        char ch = gh();
        int x = 0;
        bool t = 0;
        while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
        while (ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = gh();
        return t ? ~(x - 1) : x;
    }
    inline void wr(int x) {
        if (x < 0) {
            x = ~(x - 1);
            putchar('-');
        }
        if (x > 9)
            wr(x / 10);
        putchar(x % 10 + '0');
    }
}
using namespace vbzIO;

const int N = 2e5 + 200;
const int M = 2e7 + 200;
const int P = 998244353;
const int G = 114514;
int q, n, len = 1, lim, a[N << 2], b[N << 2], tr[N << 2], fac[M], ifac[M];

int qpow(int p, int q) {
	int res = 1;
	for (; q; q >>= 1, p = 1ll * p * p % P)
		if (q & 1) res = 1ll * res * p % P;
	return res;
}

const int iG = qpow(G, P - 2);

void init(int lim) {
	fac[0] = 1;
	for (int i = 1; i <= lim; i++) 
		fac[i] = 1ll * fac[i - 1] * i % P;
	ifac[lim] = qpow(fac[lim], P - 2) % P;
	for (int i = lim - 1; ~i; i--)
		ifac[i] = 1ll * ifac[i + 1] * (i + 1) % P;
}

int C(int n, int m) {
	if (n < m || m < 0) return 0;
	return 1ll * fac[n] * ifac[m] % P * ifac[n - m] % P;
}

void NTT(int *f, int op) {
	for (int i = 0; i < len; i++)
		if (i < tr[i]) swap(f[i], f[tr[i]]);
	for (int o = 2, k = 1; k < len; o <<= 1, k <<= 1) {
		int tg = qpow((~op) ? G : iG, (P - 1) / o);
		for (int i = 0; i < len; i += o) {
			for (int j = 0, w = 1; j < k; j++, w = 1ll * w * tg % P) {
				int x = f[i + j], y = 1ll * f[i + j + k] * w % P;
				f[i + j] = (x + y) % P, f[i + j + k] = (x - y + P) % P;
			}
		}
	}
	if (~op) return;
	int iv = qpow(len, P - 2);
	for (int i = 0; i < len; i++)
		f[i] = 1ll * f[i] * iv % P;
}

int main() {
	int t = max(rd(), rd());
	q = rd(), n = rd(), init(t);
	for (int i = 0, g = 1; i <= n; i++, g = P - g)
		a[i] = 1ll * g * ifac[i] % P, b[i] = 1ll * qpow(i, n) * ifac[i] % P;
	while (len <= (n << 1)) len <<= 1, lim++;
	for (int i = 0; i < len; i++) 
		tr[i] = (tr[i >> 1] >> 1) | ((i & 1) << (lim - 1)); 
	NTT(a, 1), NTT(b, 1);
	for (int i = 0; i < len; i++)
		a[i] = 1ll * a[i] * b[i] % P;
	NTT(a, -1);
	while (q--) {
		int x = rd(), y = rd(), z = rd();
		int res = 0;
		for (int i = 0; i <= min(min(n, x), min(y, z)); i++) 
			(res += 1ll * a[i] * fac[y] % P * ifac[y - i] % P * C(x - i, z - i) % P) %= P;
		wr(1ll * res * qpow(C(x, z), P - 2) % P), pc('\n');
	}
	return 0;
}