\(\mathcal{O}(n^3)\) 方法就不再赘述(前 \(i\) 个,\(j\) 条链,\(k\) 个不满足)。
考虑 \(a,b\) 分开来排序,从小到大,如果相同先排 \(b\)。比如说 3 2
、2 1
排成 1 2(b) 2(a) 3
,\(a\) 写成 )
,\(b\) 写成 (
,就是 (())
。那么现在问题变成了:一个袋鼠可以塞进另一个袋鼠就是两个括号严格在前面,塞完了不能还有没有被塞的可以塞前面的括号。
发现合不合法只和最靠前的没有被塞的括号有关。设 \(dp_{i,j,0/1}\) 为前 \(i\) 个括号(一共 \(2n\) 个),还有 \(j\) 个要被塞,现在有没有出现“没有被塞”这个情况了。因为是排好序的,所以后面的 (
一定可以塞得下前面的 )
。分情况转移:
- 如果是
)
。那么我们可以决定钦不钦定是不是没有被塞,那么 \(dp_{i,j+1,k}+=dp_{i-1,j,k}\) 或 \(dp_{i,j,1}+=dp_{i,j,k}\)。 - 如果是
(
。只有在 \(k=0\) 的之后我才可以不塞东西。因此 \(dp_{i,j-1,0/1}+=dp_{i-1,j,0/1}\times j\)(选择哪个塞),和 \(dp_{i,j,0}+=dp_{i-1,j,0}\)。
因此得到一个 \(\mathcal{O}(n^2)\) 的算法。
#include <bits/stdc++.h>using namespace std;using ll = long long;const int N = 5e3+3;
const ll mod = 1e9+7;ll n,dp[N][2],f[N][2];
vector<pair<int,int> > p;int main(){ios::sync_with_stdio(false);cin.tie(0);cin>>n;for (int i=1; i<=n; i++){int a,b;cin>>a>>b;p.push_back({a,1});p.push_back({b,0});}sort(p.begin(),p.end());dp[0][0]=1;for (int i=1; i<=n*2; i++){for (int j=0; j<=n; j++){f[j][0]=dp[j][0],f[j][1]=dp[j][1];dp[j][0]=dp[j][1]=0;}if (p[i-1].second){for (int j=0; j<=n; j++){for (int k=0; k<2; k++){dp[j+1][k]=(dp[j+1][k]+f[j][k])%mod;dp[j][1]=(dp[j][1]+f[j][k])%mod;}}}else{for (int j=0; j<=n; j++){for (int k=0; k<2; k++){if (j) dp[j-1][k]=(dp[j-1][k]+f[j][k]*j%mod)%mod;}dp[j][0]=(dp[j][0]+f[j][0])%mod;}}}cout<<dp[0][1]<<endl;return 0;
}