【GDOI2017模拟一试4.11】腐女的生日

37 篇文章 0 订阅
30 篇文章 0 订阅

Description

腐女要过生日了,pty 想给腐女送礼物,但是腐女所在的教室离pty 的教室太远了,于是pty就拜托会动归和A星的djy帮忙送礼物。djy在学校建立了一个平面直角坐标系,他站在了(0,0)点,腐女在(x0,y0)点,djy每次只能往上下左右四个方向移动一步,中间有n栋矩形教学楼,每个教学楼给出两个对角的坐标,并且保证每栋教学楼的周围区域(如图所示)不会有别的教学楼,即djy可以绕一个教学楼走不会碰到任何障碍,现在djy 想知道从起点到终点不碰到任何教学楼,最短需要多少步。

Input

第一行给出X0,y0;
第二行给出n;
下面一行每行x1,y1,x2,y2,表示一对对角的坐标;
保证每个矩形都不相交,且一个矩形的周围区域不会有别的矩形。

Output

输出只有一行:最短的步数

Sample Input

【样例输入1】
9 1
2
5 -3 8 3
10 -3 13 3
【样例输入2】
12 0
5
2 -1 3 1
6 -7 8 -1
6 1 8 6
4 3 4 5
10 -5 10 3

Sample Output

【样例输出1】
16
【样例输出2】
24

Data Constraint

保证所有的y坐标在[-10^6,10^6]
所有的x坐标在[0,10^6]
70%的数据保证:n<=1000
100%的数据保证:n<=10^5

Solution

这题比较神奇
所有矩形都在Y轴的右边,考虑一条从左到右,扫描线。
由题意得显然只能从左往右走,即不会往左走
那么用一颗线段树记录每一个高度的上下走最小走多少步
左右走的在统计答案时加上x0即可
那么当扫描线到达一个矩形的右边时,矩形边上的点只能从矩形两角走向中间,那么可以确定要走多远,或者说找到分界点,进行区间修改,只有单点查询

Code

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 10010000
#define M 1000001
using namespace std;
int x0,y0,n,last[N],next[N],dat1[N],dat2[N],tot=0,mi=M,ma=-M,b[N],t[N],lz[N];
void putin(int x,int y,int z)
{
    next[++tot]=last[x];last[x]=tot;dat1[tot]=y;dat2[tot]=z;
}
void down(int v)
{
    if(lz[v]==0) return;
    lz[v]=0;
    b[v*2]=b[v*2+1]=b[v];
    t[v*2]=t[v*2+1]=t[v];
    lz[v*2]=lz[v*2+1]=1;
}
int get(int v,int i,int j,int x)
{
    if(i==j) return b[v]+abs((x-M)-t[v]);
    int m=(i+j)/2;down(v);
    if(x<=m) return get(v*2,i,m,x);
    else return get(v*2+1,m+1,j,x);
}
void change(int v,int i,int j,int x,int y,int d1,int d2)
{
    if(i==x&&j==y)
    {
        lz[v]=1;b[v]=d1;t[v]=d2;
        return;
    }
    int m=(i+j)/2;down(v);
    if(y<=m) change(v*2,i,m,x,y,d1,d2);
    else if(x>m) change(v*2+1,m+1,j,x,y,d1,d2);
         else change(v*2,i,m,x,m,d1,d2),change(v*2+1,m+1,j,m+1,y,d1,d2);
}
int main()
{
    freopen("bl.in","r",stdin);
    freopen("bl.out","w",stdout);
    scanf("%d%d%d",&x0,&y0,&n);
    fo(i,1,n)
    {
        int x1,y1,x2,y2;scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        putin(x2+1,y1,y2);mi=min(mi,y1);ma=max(ma,y2);
    }
    mi--;ma++;
    fo(i,0,x0) for(int j=last[i];j;j=next[j])
    {
        int y1=dat1[j],y2=dat2[j],b1=get(1,mi+M,ma+M,y1-1+M),b2=get(1,mi+M,ma+M,y2+1+M),y=b2+y2+y1-b1;
        if(y1<=y/2) change(1,mi+M,ma+M,y1+M,y/2+M,b1+1,y1);
        if((y+1)/2<=y2) change(1,mi+M,ma+M,(y+1)/2+M,y2+M,b2+1,y2);
    }
    printf("%d\n",x0+get(1,mi+M,ma+M,y0+M));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值