題面
題解
\(m>n\)顯然無解。
建出這個序列的笛卡爾樹(如果大小相同則取最左的點),那麼一顆笛卡爾數對應且只對應一種序列。
考慮這棵笛卡爾樹的性質,就是往左兒子走它的數的大小必然減小至少\(1\),而往右走是不一定減一的。
那麼這棵笛卡爾樹必須要滿足從根往葉子節點走,向左走的次數\(\leq m\)。
考慮這個笛卡爾樹的括號序列,就是說一個點每往左走就打一個(
然後回溯回來就打一個)
,向右兒子走則不管,注意一個點如果沒有左兒子的話就直接上一個()
。
設(
權值爲\(1\),)
權值爲\(-1\),那麼這個括號序列需要滿足的要求有:\(0\leq\)前綴和\(\leq m\)。
將這個轉成格路問題,就是從\((0,0)\)走整點到\((n,n)\),其中每一步只能向右或向上走一格,且不能碰到直線\(A:y=x+1,B:y=x-m-1\),問方案數。
將連續碰到一條直線看作一次碰撞(例如\(ABBAAB\)看作\(ABAB\)),那麼可以枚舉一次碰到什麼直線,容斥計數即可。
代碼
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <tuple>
using namespace std;
const int Mod = 998244353;
const int MAX_N = 2e5 + 5;
int fpow(int x, int y) {
int res = 1;
while (y) {
if (y & 1) res = 1ll * res * x % Mod;
x = 1ll * x * x % Mod;
y >>= 1;
}
return res;
}
int N = 2e5, M, fac[MAX_N], ifc[MAX_N];
int C(int n, int m) {
if (n < 0 || m < 0 || n < m) return 0;
else return 1ll * fac[n] * ifc[m] % Mod * ifc[n - m] % Mod;
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
fac[0] = 1; for (int i = 1; i <= N; i++) fac[i] = 1ll * fac[i - 1] * i % Mod;
ifc[N] = fpow(fac[N], Mod - 2);
for (int i = N - 1; ~i; i--) ifc[i] = 1ll * ifc[i + 1] * (i + 1) % Mod;
cin >> N >> M;
if (M > N) return puts("0") & 0;
int x = N, y = N, ans = C(x + y, x);
while (x >= 0 && y >= 0) {
tie(x, y) = make_tuple(y - 1, x + 1), ans = (ans - C(x + y, x) + Mod) % Mod;
tie(x, y) = make_tuple(y + M + 1, x - M - 1), ans = (ans + C(x + y, x)) % Mod;
}
x = y = N;
while (x >= 0 && y >= 0) {
tie(x, y) = make_tuple(y + M + 1, x - M - 1), ans = (ans - C(x + y, x) + Mod) % Mod;
tie(x, y) = make_tuple(y - 1, x + 1), ans = (ans + C(x + y, x)) % Mod;
}
printf("%d\n", ans);
return 0;
}