专栏文章

P11335题解

P11335题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miq05k6h
此快照首次捕获于
2025/12/03 20:47
3 个月前
此快照最后确认于
2025/12/03 20:47
3 个月前
查看原文

科技:

set

思路:

考虑可以用一只手,先按按钮 ii 再按按钮 jj 的条件:
{TiTjAiAjTjTi\begin{cases}T_i\le T_j\\|A_i-A_j|\le T_j-T_i\end{cases}
发现二式成立时一式显然成立。将绝对值拆开,得:
{Ai+TiAj+TjTiAiTjAj\begin{cases}A_i+T_i\le A_j+T_j\\T_i-A_i\le T_j-A_j \end{cases}
那么原问题等价于将原序列拆成尽量少的子序列,使得每个子序列中的元素都二位偏序地上升。
xi=Ai+Ti,yi=TiAix_i=A_i+T_i,y_i=T_i-A_i,然后把所有元素按 xx 排序,从小到大枚举,并维护所有的子序列。
当我们枚举到 ii 时,如果此时所有子序列末尾一项的 yjy_j 都大于 yiy_i,那么我们必须新开一个子序列;
否则,找到所有满足 yjyiy_j\le y_i 的子序列中 yjy_j 最大的一个,然后将 ii 加入该子序列中。
考虑正确性:不难发现我们只关心子序列个数以及它们末尾项的 yjy_j(越小越好)。无论我们选择哪个 jj 进行操作,最终都会变成 yiy_i,而其余的 yjy_j 不变。而由于我们选择了 yjy_j 最大的一个进行操作,所以剩下的 yjy_j 肯定是更小的,因此这样操作一定是最优的。
于是我们可以用一个 set 维护所有子序列的末尾项,以便插入、删除、查前驱这些操作,时间复杂度单 log\log

代码:

CPP
#include <bits/stdc++.h>
#define pii pair <int,int>
#define fi first
#define se second
#define mkp make_pair
using namespace std;
const int N = 5e5 + 5;
int n,m,a[N],t[N];
pii p[N];
set <int> s;
set <int> :: iterator it;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= m;i++) scanf("%d",&t[i]);
	for(int i = 1;i <= m;i++) scanf("%d",&a[i]);
	for(int i = 1;i <= m;i++) p[i] = mkp(a[i] + t[i],t[i] - a[i]);
	sort(p + 1,p + m + 1);
	for(int i = 1;i <= m;i++)
	{
		if(!s.empty() && (*s.begin()) <= p[i].se)
		{
			it = s.upper_bound(p[i].se),it--;
			s.erase(it);
		}
		s.insert(p[i].se);
	}
	printf("%d\n",(int)s.size());
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...