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,y∈S 且 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 1 ≤ n ≤ 105, 1 ≤ l i ≤ v i ≤ r i ≤ 3 × 1 0 5 1 ≤ l_i ≤ v_i ≤ r_i ≤ 3\times 10^5 1 ≤ li ≤ vi ≤ ri ≤ 3×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;
}