Loading

省选训练赛 #18 题目 E 补题记录

顶级计数题。

题意:\(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\)

尝试带入转移中,对于第二种:

\[\begin{aligned} & \quad \ \sum_p \sum_q c_{p, q} \sum_k q^k \sum_a \sum_b \binom{k - a + b} a x^{p + a + 1} \frac {y^{k - a + b + 1}} {(k - a + b + 1)!} \\ &= \sum_p \sum_q c_{p, q} \sum_k \sum_s q^{k + s} \sum_{r = s} \binom rs x^{p + k + 1} \frac {y^{r + 1}} {(r + 1)!} \\ &= \sum_p \sum_q c_{p, q} x^{p + 1} \sum_k q^k x^k \sum_r \frac {y^{r + 1}} {(r + 1)!} \sum_{s \le r} \binom rs q^s \\ &= \sum_p \sum_q c_{p, q} x^{p + 1} \sum_k q^k x^k \sum_r \frac {y^{r + 1}} {(r + 1)!} (q + 1)^r \\ &= \sum_p \sum_q c_{p, q} x^{p + 1} \frac 1 {(q + 1) qx} \sum_r \frac {((q + 1)y) ^ {r + 1}} {(r + 1)!} \\ &= \sum_p \sum_q c_{p, q} x^{p + 1} \frac 1 {(q + 1) qx} (e^{(q + 1)y} - 1) \end{aligned}\]

事实上到了这步就可以开始递推了(注意第二种转移还自带一个 \(-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 __int128
	using 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;
}
posted @ 2025-01-08 07:24  Lgx_Q  阅读(2)  评论(0编辑  收藏  举报