【Codeforces 377D】Developing Game

传送门


problem

给出 n n n 个点,第 i i i 点有三个属性 l i , v i , r i l_i,v_i,r_i li,vi,ri

我们称一个集合 S S S 为合法的,当且仅当 ∀ x , y ∈ S \forall x,y\in S x,yS x = ̸ y x=\not y x≠y,有 v x ∈ [ l y , r y ] v_x∈[l_y,r_y] vx[ly,ry]

请你最大化合法集合的大小,并输出方案。

数据范围: 1   ≤   n   ≤   1 0 5 1 ≤ n ≤ 10^5 1n105 1   ≤   l i   ≤   v i   ≤   r i   ≤   3 × 1 0 5 1 ≤ l_i ≤ v_i ≤ r_i ≤ 3\times 10^5 1liviri3×105


solution

这道题的转化非常妙啊。

想想一个合法的集合 S S S,它应该满足下列性质:

{ m a x { l i } ≤ m i n { v i } m a x { v i } ≤ m i n { r i } \begin{cases} max\{l_i\}\le min\{v_i\}\\ max\{v_i\}\le min\{r_i\} \end{cases} {max{li}min{vi}max{vi}min{ri}

这就意味着, ∃ L ∈ [ m a x { l i } , m i n { v i } ] \exists L\in[max\{l_i\},min\{v_i\}] L[max{li},min{vi}] ∃ R ∈ [ m a x { v i } , m i n { r i } ] \exists R\in[max\{v_i\},min\{r_i\}] R[max{vi},min{ri}]

接下来这一步就是这道题中最关键的一步了。

在二维平面上,我们把每个点设成左下角为 ( l i , v i ) (l_i,v_i) (li,vi),右上角为 ( v i , r i ) (v_i,r_i) (vi,ri) 的矩形。这样的话,如果若干个矩形的交集不为 ∅ \varnothing ,它们就可以构成合法的集合。

所以,问题就转化成了,选出尽量多的矩形,使它们交集不为空,用扫描线处理即可。

输出方案的话,我是找到最优答案中交集里一个点的坐标,然后判断每个矩形是否包含这个点就可以了。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
using namespace std;
int n,tot,mx;
struct line{
	int x,y1,y2,sign;
}L[N];
int l[N],v[N],r[N],add[N<<2],Max[N<<2];
bool operator<(const line &p,const line &q)  {return p.x==q.x?p.sign>q.sign:p.x<q.x;}
void ADD(int root,int val)  {add[root]+=val,Max[root]+=val;}
void pushdown(int root){
	if(!add[root])  return;
	ADD(root<<1,add[root]),ADD(root<<1|1,add[root]);
	add[root]=0;
}
void modify(int root,int l,int r,int x,int y,int val){
	if(l>=x&&r<=y){
		ADD(root,val);return;
	}
	int mid=(l+r)>>1;pushdown(root);
	if(x<=mid)  modify(root<<1,l,mid,x,y,val);
	if(y>mid)  modify(root<<1|1,mid+1,r,x,y,val);
	Max[root]=max(Max[root<<1],Max[root<<1|1]);
}
int find(int root,int l,int r,int x){
	if(l==r)  return l;
	int mid=(l+r)>>1;pushdown(root);
	if(Max[root<<1]==x)  return find(root<<1,l,mid,x);
	return find(root<<1|1,mid+1,r,x);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d%d%d",&l[i],&v[i],&r[i]);
		L[++tot]=(line){l[i],v[i],r[i],1},L[++tot]=(line){v[i],v[i],r[i],-1};
		mx=max(mx,r[i]);
	}
	sort(L+1,L+tot+1);
	int ans=0,posx,posy;
	for(int i=1;i<=tot;++i){
		modify(1,1,mx,L[i].y1,L[i].y2,L[i].sign);
		if(ans<Max[1])  ans=Max[1],posx=L[i].x,posy=find(1,1,mx,ans);
	}
	printf("%d\n",ans);
	for(int i=1;i<=n;++i)
		if((l[i]<=posx&&v[i]>=posx)&&(v[i]<=posy&&r[i]>=posy))
			printf("%d ",i);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值