传送门
题面:PDF
题意:
给你一个起点s和终点e,有n个地点,问你在不增加从s到e的曼哈顿距离的前提下的能够到达的最多的地点的个数。
题目分析:
跟2018CCPC网络赛的一题(HDU 6447)一摸一样,只不过这个题中还需要讨论一下起点和终点的相对位置。
我们只需要先将y坐标离散化,并根据每个点的x坐标从小到大遍历,并且用线段树对这个点维护它的1到id[y]的最大值即可。
代码:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
struct ST{
int maxx;
}tr[maxn<<2];
struct Street{
int x,y,id;
}q[maxn];
bool cmp1(Street a,Street b){
if(a.x==b.x) return a.y<b.y;
return a.x>b.x;
}
bool cmp2(Street a,Street b){
if(a.x==b.x) return a.y>b.y;
return a.x>b.x;
}
bool cmpy(Street a,Street b){
return a.y<b.y;
}
void push_up(int rt){//线段树维护区间最大值
tr[rt].maxx=max(tr[rt<<1].maxx,tr[rt<<1|1].maxx);
}
void update(int l,int r,int rt,int C,int val){
if(l==r){
tr[rt].maxx=val;
return ;
}
int mid=(l+r)>>1;
if(C<=mid) update(l,mid,rt<<1,C,val);
else update(mid+1,r,rt<<1|1,C,val);
push_up(rt);
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&R>=r) return tr[rt].maxx;
int mid=(l+r)>>1;
int maxx=0;
if(L<=mid) maxx=max(maxx,query(L,R,l,mid,rt<<1));
if(R>mid) maxx=max(maxx,query(L,R,mid+1,r,rt<<1|1));
return maxx;
}
int main()
{
int n;
scanf("%d",&n);
Street s,e;
scanf("%d%d%d%d",&s.x,&s.y,&e.x,&e.y);
if(n==0){
puts("0");return 0;
}
for(int i=1;i<=n;i++) scanf("%d%d",&q[i].x,&q[i].y);
//坐标离散化
sort(q+1,q+1+n,cmpy);
int cnt=2,yy=q[1].y;
q[1].id=cnt;
for(int i=2;i<=n;i++){
if(yy==q[i].y) q[i].id=cnt;
else yy=q[i].y,q[i].id=++cnt;
}
if(s.x>e.x) swap(s,e);
if(s.y>e.y){//起始点在终点的左下
sort(q+1,q+1+n,cmp1);
for(int i=1;i<=n;i++){
if(q[i].x<s.x||q[i].x>e.x) continue;
if(q[i].y<e.y||q[i].y>s.y) continue;
//查询1到idy[i]的最大值
int tmp=query(1,q[i].id,1,cnt,1);
//加上一个的点对答案的影响
update(1,cnt,1,q[i].id,tmp+1);
}
}
else{//起始点在终点的右上
sort(q+1,q+1+n,cmp2);//同理上面
for(int i=1;i<=n;i++){
if(q[i].x<s.x||q[i].x>e.x) continue;
if(q[i].y<s.y||q[i].y>e.y) continue;
int tmp=query(q[i].id,cnt,1,cnt,1);
update(1,cnt,1,q[i].id,tmp+1);
}
}
cout<<query(1,cnt,1,cnt,1)<<endl;
}