AT_joisc2012_kangaroo

\(\mathcal{O}(n^3)\) 方法就不再赘述(前 \(i\) 个,\(j\) 条链,\(k\) 个不满足)。

考虑 \(a,b\) 分开来排序,从小到大,如果相同先排 \(b\)。比如说 3 22 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;
}
posted @ 2024-11-16 12:11  SFlyer  阅读(29)  评论(0)    收藏  举报