2020.06.06【省选A组】模拟

T1:首先这题可以通过推式子(考虑相邻两个位置的优劣情况)发现最优解应该是按照a/t从大到小的顺序来解题的。所以我们可以求出最优解的序列,现在要求的就是c的最大值。
但这时我们会发现可能会有多个a/t相同的点,所以我们求出每一道题可能出现的最早和最晚的时间,这两个时间就等于这个点在a/t相同的这一块中排在最前和最后的时间。
接下来我们把a从小到大排序,然后二分c,对于一个c,要满足对于每一个i(i<n)都有i的最大b值小于i+1的最小b值。因为如果这个不成立的话就一定会有一种排列顺序使得i和i+1之间出现矛盾。
到这里这题就做完了。

T2:这题有一点难度。
首先因为矩形的边是可以走的,所以最短路径一定是x单调或y单调的。
然后我们就可以通过翻转矩形来计算x单调或y单调的情况。假设现在是y单调增且sx<=tx且sy<=ty。那么我们这样来求答案:
第一要保证可以y单调增地从起点走到终点。那么我们从起点开始,优先往下走,往下走不通了之后就往右走,直到可以往下走时再往下走。按照这种策略一直走下去,当走到tx这一行时,若当前y>ty,那么就是不合法的。
保证了可以y单调之后,我们就要求最小步数。这时我们从起点开始往右走,当撞到墙了之后就往两侧分流,最终求最小值。具体做法与扫描线类似,期间用set维护。

在这里插入图片描
往两侧分流

贴一下代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<set>
#define MAXN 200010
using namespace std;

struct data
{
	int x1;
	int y1;
	int x2;
	int y2;
};
data a[MAXN],a1[MAXN];
struct dot
{
	int x,y,l;
	dot(){}
	dot(int _x,int _y,int _l){x=_x,y=_y,l=_l;}
	bool operator<(const dot&a)const{return x<a.x;}
};
set<dot>s;
set<dot>::iterator it;
struct point
{
	int x;
	int y;
	int l;
};
point b[MAXN];
int n,mx,my,sx,sy,tx,ty,ans=-1;
int add(int i,int x1,int y1,int x2,int y2)
	{a[i].x1=x1;a[i].y1=y1;a[i].x2=x2;a[i].y2=y2;}
int game(const data &a,const data &b)
{
	if(a.y1<b.y1)return 1;
	else return 0;
}
int game1(const data &a,const data &b)
{
	if(a.x1<b.x1)return 1;
	else return 0;
}
int pd(int sx,int sy,int tx,int ty)
{
	int i,j,x=sx,y=sy;
	sort(a+1,a+n+1,game1);
	for(i=1;i<=n&&x<tx;i++)
	{
		if(a[i].x1>=x&&a[i].y1<y&&y<=a[i].y2){x=a[i].x1;y=a[i].y2;}
	}
	if(y>ty)return 50;
	else return 100;
}
int work(int sx,int sy,int tx,int ty)
{
	int i,j,sum,k,mi1,mi2,v,v1,v2;
	if(!(sx<=tx&&sy<=ty))return 0;
	if(pd(sx,sy,tx,ty)==50)return 0;
	sort(a+1,a+n+1,game);
	s.clear();
	s.insert(dot(sx,sy,0));
	for(i=1;i<=n;i++)
	{
		if(sy<=a[i].y1&&a[i].y1<=ty)
		{
			j=i;
			while(j<=n&&a[j].y1==a[i].y1)
			{
				sum=0;
				while(true)
				{
					it=s.lower_bound(dot(a[j].x1,0,0));
					if(it==s.end())break;
					if((it->x)>a[j].x2)break;
					sum++;b[sum].x=(it->x);b[sum].y=(it->y);b[sum].l=(it->l);
					s.erase(it);
				}
				mi1=-1;mi2=-1;
				for(k=1;k<=sum;k++)
				{
					v1=b[k].l+a[j].y1-b[k].y+b[k].x-a[j].x1;
					if(v1<mi1||mi1==-1)mi1=v1;
					v2=b[k].l+a[j].y1-b[k].y+a[j].x2-b[k].x;
					if(v2<mi2||mi2==-1)mi2=v2;
				}
				if(sum>0)
				{
					s.insert(dot(a[j].x1,a[j].y1,mi1));
					s.insert(dot(a[j].x2,a[j].y1,mi2));
				}
				j++;
			}
			i=j-1;
		}
	}
	sum=s.size();
	while(sum>=1)
	{
		it=s.begin();
		v1=tx-(it->x);
		if(v1<0)v1=-v1;
		v=(it->l)+ty-(it->y)+v1;
		if(v<ans||ans==-1)ans=v;
		sum--;s.erase(it);
	}
}
int main()
{
//freopen("travel.in","r",stdin);
//freopen("travel.out","w",stdout);
int i,j,t;
scanf("%d %d %d %d %d",&n,&sx,&sy,&tx,&ty);
for(i=1;i<=n;i++)scanf("%d %d %d %d",&a1[i].x1,&a1[i].y1,&a1[i].x2,&a1[i].y2);
for(i=1;i<=n;i++)
{
	if(a1[i].x2>mx)mx=a1[i].x2;
	if(a1[i].y2>my)my=a1[i].y2;
}
if(sx>mx)mx=sx;if(tx>mx)mx=tx;
if(sy>my)my=sy;if(ty>my)my=ty;
for(i=1;i<=n;i++)add(i,a1[i].x1,a1[i].y1,a1[i].x2,a1[i].y2);work(sx,sy,tx,ty);
for(i=1;i<=n;i++)add(i,a1[i].x1,my-a1[i].y2,a1[i].x2,my-a1[i].y1);work(sx,my-sy,tx,my-ty);
for(i=1;i<=n;i++)add(i,mx-a1[i].x2,a1[i].y1,mx-a1[i].x1,a1[i].y2);work(mx-sx,sy,mx-tx,ty);
for(i=1;i<=n;i++)add(i,mx-a1[i].x2,my-a1[i].y2,mx-a1[i].x1,my-a1[i].y1);work(mx-sx,my-sy,mx-tx,my-ty);
for(i=1;i<=n;i++)
{
	t=a1[i].x1;a1[i].x1=a1[i].y1;a1[i].y1=t;
	t=a1[i].x2;a1[i].x2=a1[i].y2;a1[i].y2=t;
}
t=sx;sx=sy;sy=t;
t=tx;tx=ty;ty=t;
t=mx;mx=my;my=t;
for(i=1;i<=n;i++)add(i,a1[i].x1,a1[i].y1,a1[i].x2,a1[i].y2);work(sx,sy,tx,ty);
for(i=1;i<=n;i++)add(i,a1[i].x1,my-a1[i].y2,a1[i].y1,my-a1[i].y1);work(sx,my-sy,tx,my-ty);
for(i=1;i<=n;i++)add(i,mx-a1[i].x2,a1[i].y1,mx-a1[i].x1,a1[i].y2);work(mx-sx,sy,mx-tx,ty);
for(i=1;i<=n;i++)add(i,mx-a1[i].x2,my-a1[i].y2,mx-a1[i].x1,my-a1[i].y1);work(mx-sx,my-sy,mx-tx,my-ty);
printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值