构图(+BFS)——BZOJ2541/Luogu3716 [CTSC2000]冰原探险

题面:Luogu3716 BZOJ2541
无限烧脑预处理建图系列,同时也是我攻下的CTSC第一题!
只要把图能建得出来就Accepted了
所以只说建图(最后的bfs有什么好说的。。。
我们把冰山拆成四个点,冰山的每一边是一个点
然后我们暴力对每一个点(也就是冰山的边)往上下左右找最近的能撞的冰山(或者直接掉进洞里),然后连上有向边(注意有向边!!!)
当然啦,每一条边要么上下方向要么左右方向
起点再特殊处理一下好了
但是呢你建的时候还有很多要处理的东西
比如几个坑点:

  • 洞必须要特判!(下面会说)
  • 数据中坐标可能是负数(数据里好像有)
  • 数据中坐标可能爆int(这个我不太确定但是可能吧)
  • (以上两点是针对于题中未给坐标范围所致)
  • 选择上下左右建图时必须要滑过整块冰山的边(因为有可能洞在冰山的边上如果直接滑出去就会忽略洞导致输出0)
  • 对于上一点具体意思就是说往上下左右找的时候要从这条边的起始点出发而不是终点
  • 还有撞到冰山坐标还要减1或加1(这个好像忽略应该也没关系,我也不知道),然而洞就不需要

最后跑bfs,完结,撒花!
献上原来压到只剩70行的100行代码(码力啊码力)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<set>
#include<map>
#include<string>
#include<ctime>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
bool vis[100001];
struct ppap{ll xx,xy,dx,dy;}a[100001];
int dist[100001],bh[5001][5];
int nedge=0,p[100001],nex[100001],head[100001];
int n,s=1,cnt=1,t;
ll sx,sy,tx,ty;
inline void addedge(int a,int b){p[++nedge]=b;nex[nedge]=head[a];head[a]=nedge;}
inline void add(int x,int y,int z){if(y<t)addedge(x,bh[y][z]);else addedge(x,t);}
inline int down(ll x,ll y){
    int fh=0;ll min=1e18;
    for(int i=1;i<=n;i++)
        if(y>=a[i].xy&&y<=a[i].dy&&x<=a[i].xx-1&&min>a[i].xx-1)
            min=a[i].xx-1,fh=i;
    if(y==ty&&x<=tx&&min>tx)return t;
    return fh;
}
inline int left(ll x,ll y){
    int fh=0;ll max=-1e18;
    for(int i=1;i<=n;i++)
        if(x>=a[i].xx&&x<=a[i].dx&&y>=a[i].dy+1&&max<a[i].dy+1)
            max=a[i].dy+1,fh=i;
    if(x==tx&&y>=ty&&max<ty)return t;
    return fh;
}
inline int up(ll x,ll y){
    int fh=0;ll max=-1e18;
    for(int i=1;i<=n;i++)
        if(y>=a[i].xy&&y<=a[i].dy&&x>=a[i].dx+1&&max<a[i].dx+1)
            max=a[i].dx+1,fh=i;
    if(y==ty&&x>=tx&&max<tx)return t;
    return fh;
}
inline int right(ll x,ll y){
    int fh=0;ll min=1e18;
    for(int i=1;i<=n;i++)
        if(x>=a[i].xx&&x<=a[i].dx&&y<=a[i].xy-1&&min>a[i].xy-1)
            min=a[i].xy-1,fh=i;
    if(x==tx&&y<=ty&&min>ty)return t;
    return fh;
}
inline void bfs(int x){
    dist[t]=1e9;dist[x]=0;queue<int>q;q.push(x);vis[x]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int k=head[now];k;k=nex[k])if(!vis[p[k]]){
            dist[p[k]]=dist[now]+1;
            if(p[k]==t)return;vis[p[k]]=1;
            q.push(p[k]);
        }
    }
}
int main()
{
    scanf("%d%lld%lld%lld%lld",&n,&sx,&sy,&tx,&ty);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld%lld%lld",&a[i].xx,&a[i].xy,&a[i].dx,&a[i].dy);
    for(int i=1;i<=n;i++)
        for(int j=1;j<5;j++)bh[i][j]=++cnt;t=++cnt;
    if(cnt=down(sx,sy))
        add(s,cnt,1);
    if(cnt=left(sx,sy))
        add(s,cnt,2);
    if(cnt=up(sx,sy))
        add(s,cnt,3);
    if(cnt=right(sx,sy))
        add(s,cnt,4);
    for(int i=1;i<=n;i++){
        if(cnt=down(a[i].xx,a[i].dy+1))
            add(bh[i][2],cnt,1);
        if(cnt=down(a[i].xx,a[i].xy-1))
            add(bh[i][4],cnt,1);
        if(cnt=left(a[i].xx-1,a[i].dy))
            add(bh[i][1],cnt,2);
        if(cnt=left(a[i].dx+1,a[i].dy))
            add(bh[i][3],cnt,2);
        if(cnt=up(a[i].dx,a[i].dy+1))
            add(bh[i][2],cnt,3);
        if(cnt=up(a[i].dx,a[i].xy-1))
            add(bh[i][4],cnt,3);
        if(cnt=right(a[i].xx-1,a[i].xy))
            add(bh[i][1],cnt,4);
        if(cnt=right(a[i].dx+1,a[i].xy))
            add(bh[i][3],cnt,4);
    }
    bfs(1);
    if(dist[t]!=1e9)printf("%d",dist[t]);
    else puts("0");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值