洛谷P8551 Bassline 题解

P8551 Bassline 题解

分析

这道题做月赛的时候想了好久,最后发现其实很简单。

我们用样例数据来分析:

如图所示,我们将每个区间抽象化为一个一个的长条。题目给我们的两个要求可以理解为 \([x,y]\) 所覆盖的列中没有任意一个长条开始或结束,如图中第 4 列就是绿色长条的开始部分,第 7 列就是绿色长条的结束部分,它们都不满足要求,所以 \([x,y]\) 不能包含这两列。

理解到这里了,这道题就很简单了:我们只需要用有长条开始或者结束的列将最大区间分成几个部分(如下图),取区间长度减一乘以区间内的长条数的最大值即 \(k-(y-x)\) 就行了。

思路

我们使用一个数组存储每一个数(即上面分析时所说的列)是多少个区间的起点和终点,我用的是结构体 x[i] ,当然你也可以用两个数组解决。对于读入的每个区间 \([l_i,r_i]\) ,我们让 x[li].geb++,x[ri].end++ ,并且存储最左边的点 left 和最右边的点 right

接着,我们从 left 循环到 right ,用 cnt 来记录能被选择的 \([x,y]\) 区间中的元素个数(即 \(y-x\) ),每当遇到一个区间的开始或结束就将 cnt 清零,再用 k 记录每一个数上的区间个数,每次用 cnt*k 跟答案比较取最大值就好了。

程序

#include<bits/stdc++.h>
using namespace std;
int n;
struct Node
{
	int beg=0,end=0;
}x[300005];//使用结构体存储每个数字是多少个区间的起点和终点
int main()
{
	scanf("%d",&n);
	int left=300005,right=-1,k=0,cnt=0;
	for(long long i=1;i<=n;i++)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		x[l].beg++,x[r].end++;//区间[l,r]开头和结尾的数字的起点个数和终点个数分别+1
		left=min(l,left);
		right=max(right,r);
	}
	long long ans=0;//一定记得开long long,不然最后一个点会炸
	for(int i=left;i<=right;i++)
	{
		++cnt;//记录这个可选区间中y-x的值
		if(x[i].beg || x[i-1].end)
			cnt=0;//如果i是某个区间的起点或者终点就将cnt清零
		k+=x[i].beg;//用k记录i上的区间个数
		ans=max(ans,cnt*k);//取最优解
		k-=x[i].end;
	}
	printf("%lld\n",ans);
}
posted @ 2022-09-29 15:09  if-OF  阅读(76)  评论(0)    收藏  举报