我真的是***的
原本应该是乘以iLi^LiL
但是因为
iL=∑j=0L\{Lj\}(ij)j!
i^L=\sum_{j = 0}^L {L \brace j} \binom{i}{j} j!
iL=j=0∑L{jL}(ji)j!
于是这题显然就是
∑i=0ki(n−mki−i)(mi)∑j=0L\{Lj\}(ij)j!
\sum_{i = 0}^{k_i} \binom{n - m}{k_i - i} \binom{m}{i} \sum_{j = 0}^L {L \brace j} \binom{i}{j} j!
i=0∑ki(ki−in−m)(im)j=0∑L{jL}(ji)j!
然后期望就是整体除以(nki)\binom{n}{k_i}(kin)
首先交换一下求和顺序
就会变成
∑j=0L\{Lj\}j!∑i=0ki(n−mki−i)(mi)(ij)
\sum_{j = 0}^L {L \brace j} j! \sum_{i = 0}^{k_i} \binom{n - m}{k_i - i} \binom{m}{i} \binom{i}{j}
j=0∑L{jL}j!i=0∑ki(ki−in−m)(im)(ji)
而
(mi)(ij)=(mj)(m−ji−j)
\binom{m}{i} \binom{i}{j} = \binom{m}{j} \binom{m - j}{i - j}
(im)(ji)=(jm)(i−jm−j)
于是就变成了
∑j=0L\{Lj\}j!(mj)∑i=0ki(n−mki−i)(m−ji−j)
\sum_{j = 0}^L {L \brace j} j! \binom{m}{j} \sum_{i = 0}^{k_i} \binom{n - m}{k_i - i} \binom{m - j}{i - j}
j=0∑L{jL}j!(jm)i=0∑ki(ki−in−m)(i−jm−j)
后面发现上面的和为n−jn - jn−j,下面的和为ki−jk_i - jki−j,都与iii无瓜
于是就是一个范德蒙德求和的形式
就可以变成
∑j=0L\{Lj\}j!(mj)(n−jki−j)
\sum_{j = 0}^L {L \brace j} j! \binom{m}{j} \binom{n - j}{k_i - j}
j=0∑L{jL}j!(jm)(ki−jn−j)
前面的第二类斯大林数可以预处理,可以利用
\{Lj\}=∑k=0m(−1)kk!(j−k)L(j−k)!
{L \brace j} = \sum_{k = 0} ^ m \frac{(-1)^k}{k!} \frac{(j - k) ^ L}{(j - k)!}
{jL}=k=0∑mk!(−1)k(j−k)!(j−k)L
直接NTT预处理
这题比较卡常
如果直接线性预处理逆元然后做前缀积会T飞。
只有反过来用n!n!n!的逆元乘回来才可以(*的我卡常卡了一早上没想到这里慢破天际)
/* ***********************************************
Author :BPM136
Created Time :2019/8/14 9:17:59
File Name :2791.cpp
************************************************ */
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime>
#define USE_CIN_COUT ios::sync_with_stdio(0)
using namespace std;
typedef long long ll;
namespace fastIO{
#define BUF_SIZE 100000
#define OUT_SIZE 100000
//fread->read
bool IOerror=0;
inline char nc(){
static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
if (p1==pend){
p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
if (pend==p1){IOerror=1;return -1;}
//{printf("IO error!\n");system("pause");for (;;);exit(0);}
}
return *p1++;
}
inline bool blank(char ch){return ch==32||ch==10||ch==13||ch==9;}
inline bool enter(char ch){return ch==10||ch==13;}
inline void read(int &x){
bool sign=0; char ch=nc(); x=0;
for (;blank(ch);ch=nc());
if (IOerror)return;
if (ch==45)sign=1,ch=nc();
for (;ch>=48&&ch<=57;ch=nc())x=x*10+ch-48;
if (sign)x=-x;
}
inline void read(ll &x){
bool sign=0; char ch=nc(); x=0;
for (;blank(ch);ch=nc());
if (IOerror)return;
if (ch==45)sign=1,ch=nc();
for (;ch>=48&&ch<=57;ch=nc())x=x*10+ch-48;
if (sign)x=-x;
}
#undef OUT_SIZE
#undef BUF_SIZE
}
using fastIO::read;
template<typename T>
void print(T x) {
static char s[100], *s1; s1 = s;
if (!x) *s1++ = '0';
if (x < 0) putchar('-'), x = -x;
while(x) *s1++ = (x % 10 + '0'), x /= 10;
while(s1-- != s) putchar(*s1);
}
/////eval多点求值,interpolation插值
//不可传入一个系数为负数的多项式,可能在减法的时候会爆mod
//常数稍微有点大
//G是TT的原根
//NTT前记得getRevRoot
int const TT = 998244353, G = 3;
int const MAXN = 2e7 + 5;
int const MAXM = 3e5 + 5;
int const mod = TT;
int fac[MAXN], inv[MAXN];
int N, M, S, L;
inline int KSM (int a, int k) {
int ret = 1 % TT;
for (; k; k >>= 1, a = 1ll * a * a % TT)
if (k & 1)
ret = 1ll * ret * a % TT;
return ret;
}
inline int add (int x, int y) {
if (x + y >= TT)
return x + y - TT;
else
return x + y;
}
inline int sub (int x, int y) {
if (x - y >= 0)
return x - y;
else
return x - y + TT;
}
int rev[MAXM << 2], rt[MAXM << 2];
inline void getRevRoot (int n) {
int m = log(n) / log(2) + 1e-7;
for (int i = 1; i < n; ++i)
rev[i] = rev[i >> 1] >> 1 | (i & 1) << (m - 1);
for (int len = 1, uni; len < n; len <<= 1) {
uni = KSM(G, (TT ^ 1) / (len << 1));
rt[len] = 1;
for (int i = 1; i < len; ++i)
rt[i + len] = 1ll * rt[i + len - 1] * uni % TT;
}
}
inline void NTT (int* f, int n) {
for (int i = 0; i < n; ++i)
if (i < rev[i])
swap(f[i], f[rev[i]]);
for (int len = 1; len < n; len <<= 1)
for (int i = 0; i < n; i += len << 1)
for (int j = 0, x, y; j < len; ++j) {
x = f[i + j];
y = 1ll * f[i + j + len] * rt[j + len] % TT;
f[i + j] = add(x, y);
f[i + j + len] = sub(x, y);
}
}
int f[MAXM << 2], g[MAXM << 2];
inline void NTT() {
int n = 1, m = (L + 1) * 2 - 1, ivn;
while (n < m)
n <<= 1;
ivn = KSM(n, TT - 2);
getRevRoot(n);
NTT(f, n);
NTT(g, n);
for (int i = 0; i < n; ++i)
f[i] = 1ll * f[i] * g[i] % TT;
reverse(f + 1, f + n);
NTT(f, n);
for (int i = 0; i < m; ++i)
f[i] = 1ll * f[i] * ivn % TT;
}
inline void init() {
inv[0] = inv[1] = fac[0] = fac[1] = 1;
for (int i = 2; i <= N; ++i)
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[N] = KSM(fac[N], mod - 2);
for (int i = N - 1; i >= 1; --i)
inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
for (int i = 0; i <= L; ++i) {
f[i] = ((i & 1) ? TT - inv[i] : inv[i]);
g[i] = 1ll * KSM(i, L) * inv[i] % mod;
}
NTT();
}
int main() {
read(N);
read(M);
read(S);
read(L);
init();
int ans;
int n, m, K;
while (S--) {
read(n);
read(m);
read(K);
ans = 0;
int lim = min(m, min(K, L));
for (int j = 0; j <= lim; ++j) {
ans += 1ll * f[j] * inv[m - j] % mod * fac[n - j] % mod * inv[K - j] % mod;
if (ans >= mod)
ans -= mod;
}
ans = 1ll * ans * fac[K] % mod * inv[n] % mod * fac[m] % mod;
print(ans);
putchar('\n');
}
return 0;
}