CF1884C Medium Design

发布时间 2023-10-23 14:09:19作者: One_JuRuo

思路

Step1. 贪心

拿到题后,第一时间想到贪心,如果这个区间加上会使答案变小或不变就不加。

但是很显然,这个贪心是错误的。

如果答案的最大值在区间 B,但是先加了区间 A,导致加区间 B 使答案不变,那么这样就会使答案变劣。

所以贪心是错误的。

Step2. 枚举

接着,想到了可以枚举最小值,如果某个区间包含了这个最小值,那么这个区间加上后的答案一定是不优于不加上这个区间的答案,所以所有包含了这个最小值的区间都不需要加,那么再把所有最小值的答案取个最大值即可。

当然了,区间的值大,所以需要离散化。

有个巨佬朋友写过这种做法的题解

这里就不赘述了。

Step3. 优化

事实上,最小值的选择的讨论是多余的,假设最小值选在 \(x\) 点,那么所有横跨 \(x\) 的区间都是无效的,那么可以对答案做出贡献的只有两个端点都在 \(x\) 左侧或者都在右侧的区间才有用,那么假设最后的最大值在 \(x\) 左侧,那么在 \(x\) 右侧的区间无用,这样的话,把 \(x\) 右移可以让区间变多或不变,那么答案将会变优或者不变,同理最大值在 \(x\) 右侧也可以得到 \(x\) 在左端点不劣。

总而言之就是,如果 \(x\) 选在中间,那么答案一定不优于 \(x\) 选择两端的情况。

所以我们只需要讨论两种情况即可。

所以如果一个区间左端点有 \(1\) 的话,就把这个区间加给第一组线段树。

如果一个区间右端点有 \(m\) 的话,就把这个区间加给第二组线段树。

注意:一个区间可以加给两个线段树。

那么最后的答案就是两组线段树维护的最大值的较大值。

同样地,需要离散化。

AC code

#include<bits/stdc++.h>
using namespace std;
struct segtree
{
	struct node{int l,r,maxn,tag;}t[800005];
	inline void pushup(int p){t[p].maxn=max(t[p<<1].maxn,t[p<<1|1].maxn);}
	inline void addtag(int p,int k){t[p].maxn+=k,t[p].tag+=k;}
	inline void pushdown(int p){addtag(p<<1,t[p].tag),addtag(p<<1|1,t[p].tag),t[p].tag=0;}
	void build(int p,int l,int r)
	{
		t[p].l=l,t[p].r=r,t[p].maxn=t[p].tag=0;
		if(l==r) return;
		int mid=l+r>>1;
		build(p<<1,l,mid),build(p<<1|1,mid+1,r);
	}
	void update(int p,int l,int r)
	{
		if(t[p].l>=l&&t[p].r<=r){addtag(p,1);return;}
		if(t[p].tag) pushdown(p);
		int mid=t[p].l+t[p].r>>1;
		if(mid>=l) update(p<<1,l,r);
		if(mid<r) update(p<<1|1,l,r);
		pushup(p);
	}
}t1,t2;
int T,n,m,l[200005],r[200005],a[400005],num;
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;++i) scanf("%d%d",&l[i],&r[i]),a[i*2-1]=l[i],a[i*2]=r[i];a[n*2+1]=1,a[n*2+2]=m;
		sort(a+1,a+2*n+3),num=unique(a+1,a+2*n+3)-a-1;
		t1.build(1,1,num),t2.build(1,1,num);
		//cout<<num<<endl;
		for(int i=1;i<=n;++i)
		{
			l[i]=lower_bound(a+1,a+num+1,l[i])-a,r[i]=lower_bound(a+1,a+num+1,r[i])-a;
			if(l[i]!=1) t1.update(1,l[i],r[i]);
			if(r[i]!=num) t2.update(1,l[i],r[i]);
		}
		printf("%d\n",max(t1.t[1].maxn,t2.t[1].maxn));
	}
	return 0;
}

Step4. 进一步优化

可以发现,只需要区间修改和一次区间查询,所以实际上可以直接使用差分数组进行维护,如果不算上离散化要用到的排序的话,时间复杂度更优,代码也更简单。

主要是因为前面的方法需要线段树,所以最开始没想到差分,辛辛苦苦写完了才发现更本不需要。

因为代码难度低,所以这里就不放差分版的代码了 (绝对不是我懒得再写一个差分了)