qoj8528 Chords 题解

数据随机有什么用?用你惊人的注意力可以观察到答案的值域在 \(800\) 附近。

考虑暴力 dp,设 \(dp_{l,r}\) 表示值域在 \([l,r]\) 中最多能选几个区间。假设 \(r\) 对应区间的左端点为 \(lst\),那么有转移方程 \(dp_{l,r}=dp_{l,lst-1}+dp_{lst+1,r-1}+1\)。时间复杂度 \(O(n^2)\)

因为答案很小,考虑交换 dp 答案和状态。设 \(dp_{r,i}\) 表示值域右端点在 \(r\),答案为 \(i\) 时左端点最大值是多少。对于每一个 \(r\),我们可以先二分求出 \([lst+1,r-1]\) 中最多可以选 \(x\) 个区间,那么有转移方程:\(dp_{r,i} = \max(dp_{r-1,i},dp_{lst-1,i-x-1})\)。时间复杂度 \(O(n \log V + nV)\),实测 \(V\)\(1000\) 能过。

//dzzfjldyqqwsxdhrdhcyxll
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f
const int MAXN = 1e5 + 10;
int n,l[MAXN],r[MAXN],lst[2 * MAXN],dp[2 * MAXN][1205];
signed main() {
	cin >> n;
	for(int i = 1;i <= n;i++) {
		cin >> l[i] >> r[i];
		lst[r[i]] = l[i];
	}
	memset(dp,-inf,sizeof dp);
	dp[0][0] = 0;
	for(int i = 1;i <= 2 * n;i++) {
		dp[i][0] = i;
		if(lst[i] == 0) {
			for(int j = 1;j <= 1200;j++) dp[i][j] = dp[i - 1][j];
		} else {
			for(int j = 1;j <= 1200;j++) dp[i][j] = dp[i - 1][j];
			int L = lst[i] + 1,R = i - 1;
			int ans = 0,ll = 0,rr = 1200;
			while(ll <= rr) {
				int mid = (ll + rr) >> 1;
				if(dp[R][mid] >= L) ans = mid,ll = mid + 1;
				else rr = mid - 1;
			}
			ans++;
			for(int j = 1;j <= 1200;j++) {
				if(ans >= j) dp[i][j] = max(dp[i][j],lst[i]);
				else dp[i][j] = max(dp[i][j],dp[lst[i]][j - ans]);
			}
		}
	}
	for(int i = 1200;i >= 0;i--) {
		if(dp[2 * n][i] >= 1) {
			cout << i;
			return 0;
		}
	}
	return 0;
}
posted @ 2024-10-14 10:31  Creeper_l  阅读(14)  评论(0编辑  收藏  举报