顶级计数题。
题意:\(T\) 组询问,给出 \(n, m\),求有多少个长度为 \(n\) 且值域为 \([1, m]\) 的序列 \(a\),满足不存在 \(1\le i < j\le n\) 使得 \(\max\limits_{k = 1} ^ i a_k = \min\limits_{k = j} ^ n a_k\)。
\(T\le 10^5, \ n\le 300, \ m\le 10^9\)
相当于对于每一种数字 \(x\),不同时满足:出现超过一次,第一次出现位置之前全部小于 \(x\),最后一次出现位置之后全部大于 \(x\)。
考虑 插入DP + 容斥,设 \(f_{i, j, k}\) 表示从小到大已经填了 \(i\) 种数字,填了 \(j + k\) 个数,并且前 \(j\) 个空隙以后不能填数字,的方案数。
考虑填 \(i + 1\) 的转移,不限制时,枚举 \(a\) 表示填的个数,转移:\(f_{i + 1, j, k + a} \gets \dbinom {k + a} a f_{i, j, k}\)。
当填的个数 \(\ge 2\),填过最后一个空隙,并且第一个填的位置之前以后再也不填数字,时不合法。具体的,枚举填了 \(b + 2\) 个数,其中第一个数前面有 \(j + a\) 个数,转移:\(f_{i + 1, j + a + 1, k - a + b + 1} \gets -\dbinom {k - a + b} b f_{i, j, k}\)。
时间复杂度 \(\mathcal O(n^5)\)。
考虑 GF 优化。注意到 \(k\) 和组合数关系较大,将其设为 EGF,设 \(F_i = \sum\limits_j \sum\limits_k f_{i, j, k} x^j \dfrac {y^k} {k!}\)。
第一种转移:\(F_{i + 1} \gets F_i (e^y - 1)\)。
第二种转移:\(F_{i + 1} \gets -\sum\limits_j \sum\limits_k f_{i, j, k} \sum\limits_a \sum\limits_b \dbinom{k - a + b} a x^{j + a + 1} \dfrac {y^{k - a + b + 1}} {(k - a + b + 1)!}\)。
但是依然很难优化,考虑科技方法:我们断言 \(F_i\) 可以表示为 \(F_i = \sum\limits_p \sum\limits_q c_{i, p, q} x^p e^{qy}\)。
这样,容易得到 \(f_{i, j, k} = \sum\limits_q c_{j, q} q^k\)。
尝试带入转移中,对于第二种:
事实上到了这步就可以开始递推了(注意第二种转移还自带一个 \(-1\) 系数)。
-
\(-c_{i, p, q} \times q^t \to c_{i + 1, p + 1 + t, q + 1}\)
-
\(c_{i, p, q} \times q^t \to c_{i + 1, p + 1 + t, 0}\)
对于第一种转移,即乘上一个 \((e^y - 1)\):
-
\(c_{i, p, q} \to c_{i + 1, p, q + 1}\)
-
\(-c_{i, p, q} \to c_{i + 1, p, q}\)
点击查看代码
#include <bits/stdc++.h>namespace Initial {#define ll long long#define ull unsigned long long#define fi first#define se second#define mkp make_pair#define pir pair <ll, ll>#define pb push_back#define i128 __int128using namespace std;const ll maxn = 310, inf = 1e13, mod = 998244353, L = 1e7 + 10;ll power(ll a, ll b = mod - 2, ll p = mod) {ll s = 1;while(b) {if(b & 1) s = 1ll * s * a %p;a = 1ll * a * a %p, b >>= 1;} return s;}template <class T>const inline ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }template <class T>const inline void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }template <class T>const inline void chkmax(T &x, const T y) { x = x < y? y : x; }template <class T>const inline void chkmin(T &x, const T y) { x = x > y? y : x; }
} using namespace Initial;namespace Read {char buf[1 << 22], *p1, *p2;// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)template <class T>const inline void rd(T &x) {char ch; bool neg = 0;while(!isdigit(ch = getchar()))if(ch == '-') neg = 1;x = ch - '0';while(isdigit(ch = getchar()))x = (x << 1) + (x << 3) + ch - '0';if(neg) x = -x;}
} using Read::rd;ll t, n, m, c[maxn][maxn][maxn], inv[maxn];
ll f[maxn][maxn][maxn], res[maxn][maxn];int main() {c[0][0][0] = inv[1] = 1;for(ll i = 2; i <= 301; i++)inv[i] = (mod - mod / i) * inv[mod % i] %mod;for(ll i = 1; i <= 300; i++) {for(ll p = 0; p <= 300; p++)for(ll q = 0; q <= 300; q++) {add(c[i][p][q + 1], c[i - 1][p][q]);add(c[i][p][q], mod - c[i - 1][p][q]);}for(ll q = 0; q <= 300; q++)for(ll p = 0, w = 0; p <= 300; p++) {add(c[i][p][q + 1], mod - w * inv[q + 1] %mod);add(c[i][p][0], w * inv[q + 1] %mod);w = (w * q + c[i - 1][p][q]) %mod;}}for(ll i = 1; i <= 300; i++)for(ll q = 0; q <= 300; q++)for(ll p = 0, w = 0; p <= 300; p++) {w = (w * q + c[i][p][q]) %mod;add(res[i][p], w);}rd(t);while(t--) {rd(n), rd(m); ll ans = 0, w = 1;for(ll i = 1; i <= n && i <= m; i++) {w = inv[i] * (m - i + 1) %mod * w %mod;ans = (ans + w * res[i][n]) %mod;}printf("%lld\n", ans);}return 0;
}