Gym102538 300iq Contest 3 部分补题记录 || 杨氏矩阵学习笔记

目录

文章目录

  • 目录
  • 前言
  • D.Disjoint Lis
    • 前置知识:杨氏矩阵
      • 杨表和排列的对应关系:

前言

我是傻逼

D.Disjoint Lis

题意:定义一个排列是“好”的,如果在这个排列中能找到两个不相交的最长上升子序列。

给定 n n n,求长为 n n n的"好"排列个数对998244353取模的结果。
n < = 75 n<=75 n<=75

前置知识:杨氏矩阵

参考链接
杨氏矩阵又叫杨表 满足:

  1. 若(i,j)为空,则它右边和下边的位置也一定为空
  2. 若(i,j)有元素w,则它右边和下边要么为空,要么有比w大的元素
  3. 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(n1)+(n1)f(n2),(n>2)
  4. 钩子公式:对于给定形状,不同的杨氏矩阵的个数为: n ! n! n!除以每个格子的钩子长度加一的积,其中钩子长度的定义为该格子右边的格子数和它上边的格子数之和

e.g poj1825 给定如下形状
x x x x x
x x x
x x x
x
把1~12填入x所在位置,从上到下,从左到右都是按小到大排列,有几种排法
直接套钩子公式

杨氏矩阵可以支持以下操作:

  1. 查找值为x的元素 根据元素大小关系从左上角出发移动就行
  2. 查找第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 Sx表示将 x x x从第一行插入杨表中,具体做如下操作:

  1. 在当前行中找到最小的比 x x x大的数 y y y
  2. 如果找到了就用 x x x去替换 y y y,移动到下一行,令 x ← y x←y xy重复操作1.
  3. 如果找不到,把 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 xS表示将 x x x从第一列插入杨表中。
有引理: S ← x S←x Sx x → S x→S xS一定是一个杨表。

对于一个排列 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! λNfλ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) (n2×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;
}

你可能感兴趣的:(杨氏矩阵,勾长公式,codeforces,icpc,数学)