目录
前言
我是傻逼
D.Disjoint Lis
题意:定义一个排列是“好”的,如果在这个排列中能找到两个不相交的最长上升子序列。
给定
n
n
n,求长为
n
n
n的"好"排列个数对998244353取模的结果。
n
<
=
75
n<=75
n<=75
前置知识:杨氏矩阵
参考链接
杨氏矩阵又叫杨表 满足:
- 若(i,j)为空,则它右边和下边的位置也一定为空
- 若(i,j)有元素w,则它右边和下边要么为空,要么有比w大的元素
- 1~n组成的杨氏矩阵的个数: f ( 1 ) = 1 , f ( 2 ) = 2 , f ( n ) = f ( n − 1 ) + ( n − 1 ) f ( n − 2 ) , ( n > 2 ) f(1)=1,f(2)=2,f(n)=f(n-1)+(n-1)f(n-2),(n>2) f(1)=1,f(2)=2,f(n)=f(n−1)+(n−1)f(n−2),(n>2)
- 钩子公式:对于给定形状,不同的杨氏矩阵的个数为: n ! n! n!除以每个格子的钩子长度加一的积,其中钩子长度的定义为该格子右边的格子数和它上边的格子数之和
e.g poj1825 给定如下形状
x x x x x
x x x
x x x
x
把1~12填入x所在位置,从上到下,从左到右都是按小到大排列,有几种排法
直接套钩子公式
杨氏矩阵可以支持以下操作:
- 查找值为x的元素 根据元素大小关系从左上角出发移动就行
- 查找第k大的元素 二分一个比矩阵中k个数大的数x,然后用1中查找法找到最大的比他小的数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 x x大的数 y y y
- 如果找到了就用 x x x去替换 y y y,移动到下一行,令 x ← y x←y x←y重复操作1.
- 如果找不到,把 x x x放在该行末尾并退出。记 x x x在第 s s s行第 t t t列, ( s , t ) (s,t) (s,t)必定是一个边角。一个格子 ( s , t ) (s,t) (s,t)是边角当且仅当(s+1,t)和(s,t+1)都不存在格子。
我们可以类似地定义对列插入算法。
定义
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<bits/stdc++.h>
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;
}