\(\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;
}
标签:joisc2012,int,kangaroo,括号,mathcal,using,ll,dp
From: https://www.cnblogs.com/SFlyer/p/18549242