題意
設表示序列中,中最大值的位置。若存在若干個最大值,則取最靠前的那一個。定義兩個序列同構,當且僅當兩個序列長度相等,且對於任意都有。問有多少個長度爲且不同構的序列,其中的元素爲到中的整數,且每個整數在其中至少出現一次。
分析
有一種挺妙的生成函數做法,可以看這篇博客。
若我們對序列建笛卡爾樹,容易發現兩個序列同構,當且僅當它們的笛卡爾樹形狀相同。並且不同構的序列,和最長左鏈(某個節點到根的路徑中,作爲左兒子的次數+1)不超過的笛卡爾樹是一一對應的。問題轉化成求個節點且最長左鏈不超過的笛卡爾樹數量。
通過多叉樹轉二叉樹的方法(二叉樹的左兒子代表該節點在多叉樹上最左邊的兒子,右兒子代表該節點在多叉樹上右邊第一個兄弟),將個節點的二叉樹與個節點(要補一個根)的多叉樹一一對應,且二叉樹的最長左鏈不超過,當且僅當其對應的多叉樹的深度不超過(根節點深度爲)。問題轉化爲求個節點深度不超過的多叉樹數量。
將個節點多叉樹的入棧出棧序看成括號序,就是形如,其中是由對括號組成的合法括號序。將左括號看成,右括號看成,該多叉樹深度不超過當且僅當括號徐的每個前綴均不超過。問題轉化爲求對括號組成且前綴不超過的合法括號序數量。
用類似證明卡特蘭數的思想,將其放到二維平面上,變爲求從走到,每次橫座標,縱座標或,且不經過和的路徑數量。這個可以用容斥來算。具體來說,設其先經過若干次,再經過若干次,再經過若干次,如此類推。則把起點沿對稱,再沿對稱,再沿對稱,如此類推。再計算最終得到的起點到的路徑數量,就是對應的方案數。容斥係數爲。
時間複雜度。
代碼
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200005;
const int MOD = 998244353;
int n, m, jc[N], ny[N];
int C(int n, int m)
{
return (LL)jc[n] * ny[m] % MOD * ny[n - m] % MOD;
}
int main()
{
scanf("%d%d", &n, &m);
if (n < m) {puts("0"); return 0;}
m++;
jc[0] = jc[1] = ny[0] = ny[1] = 1;
for (int i = 2; i <= n * 2; i++) jc[i] = (LL)jc[i - 1] * i % MOD, ny[i] = (LL)(MOD - MOD / i) * ny[MOD % i] % MOD;
for (int i = 2; i <= n * 2; i++) ny[i] = (LL)ny[i - 1] * ny[i] % MOD;
int ans = C(n * 2, n);
for (int i = 0, x = 0; ; i++)
{
if (i & 1) x = -2 - x;
else x = m * 2 - x;
if (abs(x) > n * 2) break;
if (i & 1) (ans += C(n * 2, (n * 2 - x) / 2)) %= MOD;
else (ans += MOD - C(n * 2, (n * 2 - x) / 2)) %= MOD;
}
for (int i = 0, x = 0; ; i++)
{
if (i & 1) x = m * 2 - x;
else x = -2 - x;
if (abs(x) > n * 2) break;
if (i & 1) (ans += C(n * 2, (n * 2 - x) / 2)) %= MOD;
else (ans += MOD - C(n * 2, (n * 2 - x) / 2)) %= MOD;
}
printf("%d\n", ans);
return 0;
}