Luogu

分析

首先容易看出一个暴力,即每个点向能到达的点连边,然后最短路即可。

然而边数是 $\mathcal{O}(n^2)$ 级别的,我们需要优化连边。由于是向矩形范围内的点连边,所以考虑 KD-Tree 优化连边。

对于城市 $u$ 向矩形连的边,我们在 KD-Tree 上遍历:如果当前节点范围包含在矩形内,则 $u$ 直接向这个节点连边;如果当前节点范围与矩形无交,则直接返回;否则,如果这个节点表示的城市在矩形中则 $u$ 向这个节点表示的城市连边,然后递归处理左右儿子。

然后我们将 KD-Tree 上的所有节点向它的左右儿子和它表示的城市连 $0$ 边即可。

这样子空间会炸,所以我们可以不显式地连出边,而是使用 Dijkstra 并直接在 KD-Tree 上进行松弛。

有一个优秀的剪枝:当 KD-Tree 上某个节点的 $dis$ 小于等于当前松弛的 $dis$ 时可以直接返回,因为此时子树不可能被松弛。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=70000+10,M=150000+10;

int n,m,w,h,D;
struct point { int x[2],id; } p[N];
bool operator <(point a,point b) { return a.x[D]<b.x[D]; }
struct rec { int l,r,d,u,t; };
vector<rec> E[N];

#define ls(x) t[x].lc
#define rs(x) t[x].rc
int rt,tot=0,pos[N<<1];
struct node { point p; int lc,rc,mn[2],mx[2]; } t[N<<1];
void pushup(int o) {
    for (int i=0;i<2;++i) {
        t[o].mn[i]=t[o].mx[i]=t[o].p.x[i];
        if (ls(o)) {
            t[o].mn[i]=min(t[o].mn[i],t[ls(o)].mn[i]);
            t[o].mx[i]=max(t[o].mx[i],t[ls(o)].mx[i]);
        }
        if (rs(o)) {
            t[o].mn[i]=min(t[o].mn[i],t[rs(o)].mn[i]);
            t[o].mx[i]=max(t[o].mx[i],t[rs(o)].mx[i]);
        }
    }
}
int build(int l,int r,int d) {
    if (l>r) return 0;
    int o=++tot,mid=(l+r)>>1;
    D=d,nth_element(p+l,p+mid,p+r+1),t[o].p=p[mid],pos[p[mid].id]=o;
    ls(o)=build(l,mid-1,d^1),rs(o)=build(mid+1,r,d^1);
    pushup(o); return o;
}

bool outside(node t,int L,int R,int D,int U) {
    return t.mx[0]<L||t.mn[0]>R||t.mx[1]<D||t.mn[1]>U;
}
bool inside(node t,int L,int R,int D,int U) {
    return t.mn[0]>=L&&t.mx[0]<=R&&t.mn[1]>=D&&t.mx[1]<=U;
}
bool inside(point p,int L,int R,int D,int U) {
    return p.x[0]>=L&&p.x[0]<=R&&p.x[1]>=D&&p.x[1]<=U;
}

struct Hnode { int u,d; };
bool operator <(Hnode a,Hnode b) { return a.d>b.d; }
priority_queue<Hnode> Q; 
int dis[N<<1];
void relax(int v,int d) { if (d<dis[v]) dis[v]=d,Q.push((Hnode){v,d}); }
void relax(int o,int L,int R,int D,int U,int d) {
    if (d>=dis[o+n]) return;
    if (outside(t[o],L,R,D,U)) return;
    if (inside(t[o],L,R,D,U)) { relax(o+n,d); return; }
    if (inside(t[o].p,L,R,D,U)) relax(t[o].p.id,d);
    if (ls(o)) relax(ls(o),L,R,D,U,d);
    if (rs(o)) relax(rs(o),L,R,D,U,d);
}
void dijkstra() {
    memset(dis,0x3f,sizeof(dis)),dis[1]=0,Q.push((Hnode){1,0});
    while (!Q.empty()) {
        int u=Q.top().u,d=Q.top().d; Q.pop();
        if (dis[u]!=d) continue;
        if (u<=n) {
            for (auto i:E[u]) relax(rt,i.l,i.r,i.d,i.u,dis[u]+i.t);
        } else {
            relax(t[u-n].p.id,dis[u]);
            if (ls(u-n)) relax(ls(u-n)+n,dis[u]);
            if (rs(u-n)) relax(rs(u-n)+n,dis[u]);
        }
    }
}

int main() {
    n=read(),m=read(),w=read(),h=read();
    for (int i=1;i<=n;++i) p[i].x[0]=read(),p[i].x[1]=read(),p[i].id=i;
    rt=build(1,n,0);
    for (int i=1;i<=m;++i) {
        int p=read(),t=read(),l=read(),r=read(),d=read(),u=read();
        E[p].emplace_back((rec){l,r,d,u,t});
    }
    dijkstra();
    for (int i=2;i<=n;++i) printf("%d\n",dis[i]);
    return 0;
}
最后修改:2020 年 07 月 26 日 07 : 52 PM