我是傻逼
题意:定义一个排列是“好”的,如果在这个排列中能找到两个不相交的最长上升子序列。
给定 n n n,求长为 n n n的"好"排列个数对998244353取模的结果。
n < = 75 n<=75 n<=75
参考链接
杨氏矩阵又叫杨表 满足:
e.g poj1825 给定如下形状
x x x x x
x x x
x x x
x
把1~12填入x所在位置,从上到下,从左到右都是按小到大排列,有几种排法
直接套钩子公式
杨氏矩阵可以支持以下操作:
记 λ = ( λ 1 , ⋯ , λ m ) ( λ 1 ≥ λ 2 ≥ ⋯ ≥ λ m > 0 ) \lambda=(\lambda_1,\cdots,\lambda_m)(\lambda1≥\lambda_2≥\cdots≥\lambda_m>0) λ=(λ1,⋯,λm)(λ1≥λ2≥⋯≥λm>0)是n的一个整数拆分,记为 λ ⊢ N λ ⊢ N λ⊢N
RSK算法:
参考:IOI2019集训队论文 《浅谈杨氏矩阵在信息学竞赛中的应用》
插入算法:
令 S S S是一个杨表,定义 S ← x S←x S←x表示将 x x x从第一行插入杨表中,具体做如下操作:
我们可以类似地定义对列插入算法。
定义 x → S x→S x→S表示将 x x x从第一列插入杨表中。
有引理: S ← x S←x S←x和 x → S x→S x→S一定是一个杨表。
对于一个排列 X = ( x 1 , x 2 , ⋯ , x k ) X=(x_1,x_2,\cdots,x_k) X=(x1,x2,⋯,xk)定义 P x P_x Px为杨表, Q x Q_x Qx为记录表,即在对 P x P_x Px插入 x i x_i xi时将下标的 i i i插入对应位置并维持 Q x Q_x Qx与 P x P_x Px的形状相同。显然, Q x Q_x Qx也是一个杨表。
定理:一个1到n的排列 X = x 1 , x 2 , ⋯ , x n X = x_1,x_2,\cdots,x_n X=x1,x2,⋯,xn和一对相同形状的标准杨表是一一对应的。也就是说, ∑ λ ⊢ N f λ 2 = n ! \sum_{λ ⊢ N}f_{\lambda}^{2}=n! λ⊢N∑fλ2=n!
定义k-LIS序列为LIS长度不超过k的序列
同理可以定义k-LDS序列为LDS不超过k的序列
显然最长的1-LIS子序列就是该序列的LDS(杨表第一列)
最长的k-LIS长度就是前k列长度总和,最长的k-LDS长度就是前k行的长度总和。
那么根据Dilworth定理,本题中要求的就是2-LDS=2*LIS长度的排列个数
构造杨表 第一行和第二行长度都为L 求这样的杨表的个数就行了
也就是说 λ 1 = λ 2 = L \lambda_1=\lambda_2=L λ1=λ2=L ,枚举这个 L L L,把剩下的 ( n − 2 × L ) (n-2\times L) (n−2×L)拿去划分一下 就可以用钩子公式得到总数了
#include
using namespace std;
#define ll long long
const int maxn = 80, md = 998244353;
ll r[maxn], c[maxn], ans, n, fac[maxn], inv[maxn];
ll ksm(ll a, ll b) {
ll res = 1;
while (b) {
if (b & 1) res = res * a % md;
a = a * a % md;
b >>= 1;
}
return res;
}
void dfs(int cur, int rem, int lim) {
if (rem == 0) {
memset(c, 0, sizeof(c));
for (int i = 1; i < cur; i++) c[r[i]]++;
for (int i = n-1; i > 0; i--) c[i] += c[i+1];
ll res = fac[n];
for (int i = 1; i < cur; i++)
for (int j = 1; j <= r[i]; j++)
res = res * inv[r[i]+c[j]-j-i+1] % md;
ans = (ans + res * res % md) % md;
return;
}
for (int i = 1; i <= min(rem, lim); i++) {
r[cur] = i;
dfs(cur + 1, rem - i, i);
}
}
int main() {
cin >> n;
fac[0] = inv[1] = 1;
for (ll i = 1; i <= n; i++) fac[i] = fac[i-1]*i%md;
for (int i = 2; i <= n; i++) inv[i] = ksm(i, md-2);
int t = n / 2;
for (int i = 1; i <= t; i++) {
r[1] = r[2] = i;
dfs(3, n-2*i, i);
}
cout << ans << endl;
return 0;
}