http://codeforces.com/gym/101987/attachments A
题意: 给出多个线段,你选择两个点,与最多的线段相交(与两个点相交只算一次)。
解析: 随着第一个点的后移,第二个点需要考虑的线段越来越少,如果按照左端点排序,发现每次被第一个点解除的线段从前往后。所以只需要考虑每个后缀的最优策略即可。
代码:
#include
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
const int maxn=3e5+9;
struct node{
int l,r;
bool operator<(const node&A)const{
return l<A.l;
}
}e[maxn];
struct Uni{
int num;
int tmp[maxn];
void push(int x){
tmp[++num]=x;
}
void init(){
sort(tmp+1,tmp+1+num);
num=unique(tmp+1,tmp+1+num)-tmp-1;
}
int idx(int val){
return lower_bound(tmp+1,tmp+1+num,val)-tmp;
}
int ori(int idx){
return tmp[idx];
}
}U;
/*_________________________________________________________unique*/
int tr[maxn<<2];//max
int laz[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
#define root int rt,int l,int r
#define lson ls,l,mid
#define rson rs,mid+1,r
void down(root){
if(laz[rt]){
laz[ls]+=laz[rt];
laz[rs]+=laz[rt];
tr[ls]+=laz[rt];
tr[rs]+=laz[rt];
laz[rt]=0;
}
}
void update(root,int L,int R,int v){
if(l>=L&&r<=R){
tr[rt]+=v;
laz[rt]+=v;
return;
}
down(rt,l,r);
if(L<=mid)update(lson,L,R,v);
if(R>mid)update(rson,L,R,v);
tr[rt]=max(tr[ls],tr[rs]);
}
int query(root,int L,int R){
if(l>=L&&r<=R){
return tr[rt];
}
down(rt,l,r);
int ans=0;
if(L<=mid)ans=query(lson,L,R);
if(R>mid)ans=max(ans,query(rson,L,R));
return ans;
}
int ct[maxn];
int mx;
int ans[maxn];
int main(){
int n;scanf("%d",&n);
int _;
rep(i,1,n){
scanf("%d%d%d%d",&_,&e[i].r,&_,&e[i].l);
U.push(e[i].l);
U.push(e[i].r);
}
U.init();
mx=U.num;
rep(i,1,n)e[i].l=U.idx(e[i].l),e[i].r=U.idx(e[i].r);
rep(i,1,n){
ct[e[i].l]++;
ct[e[i].r+1]--;
}
rep(i,1,mx)ct[i]+=ct[i-1];
sort(e+1,e+1+n);
per(i,n,1){
update(1,1,mx,e[i].l,e[i].r,1);
ans[i]=query(1,1,mx,1,mx);
}
ans[n+1]=0;
int ar=1;
int Ans=0;
for(int i=1;i<=mx;i++){
while(ar<=n&&e[ar].l<=i)ar++;
Ans=max(Ans,ct[i]+ans[ar]);
}
printf("%d\n",Ans);
}