1. Dilworth定理
Dilworth定理由数学家Robert P. Dilworth于1950年提出,它描述了偏序集中链和反链之间的关系。
- 偏序集:一个集合 equipped with a partial order(即一个自反、反对称、传递的关系)。
- 链:偏序集的一个子集,其中任意两个元素都是可比较的(即全序子集)。
- 反链:偏序集的一个子集,其中任意两个元素都是不可比较的。
Dilworth定理:在任意有限偏序集中,其最小链划分的大小(即将偏序集分解成链的最小数量)等于其最大反链的大小(即反链中元素的最大数量)。
例子:考虑一个序列 [3, 1, 2],其中偏序关系基于元素值和位置(例如,元素值越小越“小”,但位置也影响)。Dilworth定理可以帮助我们找到最小链分解。
2. 序列分解定理
序列分解定理是Dilworth定理在序列上的一个特例。它关注的是序列能否被分解为k个非递减子序列。
- 非递减子序列:一个子序列其中每个元素都不小于前一个元素(即单调非递减)。
- 分解:将原序列的每个元素分配到k个子序列中,使得每个子序列都是非递减的。
序列分解定理:一个序列可以被分解为k个非递减子序列 当且仅当 序列中最长递减子序列的长度不超过k。
这意味着:
- 如果序列中存在一个长度为k+1的递减子序列,那么它无法被分解成k个非递减子序列。
- 反之,如果最长递减子序列的长度为k,那么序列可以被分解成k个非递减子序列。
Dilworth定理为这个结论提供了严格的数学基础:在序列构成的偏序集(基于值和位置)中,反链对应递减子序列,链对应非递减子序列。
3. 定理在算法题中的应用 (CF-2143-D1)
题目链接:[[https://codeforces.com/contest/2143/problem/D1]]
这个题需要统计所有“好”子序列(即可以被分解为两个非递减子序列的子序列)。根据序列分解定理,这等价于统计所有最长递减子序列长度不超过2的子序列。这可以用dp快速解决:记dp[i][j]
(其中 i
和 j
表示两个非递减链的最后一个元素值,且 i<=j
)为子序列的数量。对于每个新元素 v
,我们检查是否能将其添加到第一个链(如果 v>=i
)或第二个链(如果 v>=j
),并更新状态,最后统计即可。
直观上,如果序列中有三个元素递减(如 a > b > c 且顺序出现),那么这三个元素无法被分配到两个非递减链中(因为每个链必须非递减,但递减关系要求它们分属不同链,但只有两个链,矛盾)。反之,如果没有这样的三个元素,那么总可以通过贪心将序列分解为两个非递减链。
AC代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9+7;signed main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);auto solve = [&](){int n;cin >> n;vector<int> a(n);for (int i=0; i<n; i++) cin >> a[i];vector<vector<int>> dp(n+1, vector<int>(n+1, 0));dp[0][0] = 1; // 空序列for (auto v: a){vector<vector<int>> cpydp = dp;for(int i=0; i<=n; i++){for(int j=0; j<=n; j++){if(dp[i][j] == 0) continue;if(v >= i)cpydp[v][j] = (cpydp[v][j]+dp[i][j])%mod;else if(v >= j)cpydp[i][v] = (cpydp[i][v]+dp[i][j])%mod;}}dp = cpydp;}int ans = 0;for(int i=0; i<=n; i++)for(int j=0; j<=n; j++)ans = (ans+dp[i][j])%mod;cout << ans << "\n";};int T;for(cin>>T; T--; ) solve();return 0;
}