[NOI2019] 弹跳

题意:

给你平面上的$n$个点,共有$m$个弹跳装置。

每个弹跳装置可以从点$p_{i}$以$t_{i}$的代价跳到矩形$(L_{i},D_{i}),(R_{i},U_{i})$中的任何一个点。

现在请你对于每座城市求出从1号点跳到它的最小代价。

$n\leq 70000,m\leq 150000$。

 

题解:不把边建出来的$KD-tree$优化建图。

看一眼就知道$KD-tree$优化建图,但如果把所有边都建出来就爆炸了。

设原图上的点是实点,$KD-tree$上的点(代表一个实点和一个矩形)是虚点。

那么在$Dijkstra$到每个点的时候:

  • 若是实点,在$KD-tree$上查找能连的虚点/实点并向其连带权边。
  • 若是虚点,向它的两个儿子和它对应的实点连权为0的边。

时间复杂度$O(m\sqrt{n})$,空间复杂度$O(m\sqrt{n})$(实际上只有优先队列可能达到这个空间,其他都是$O(n)$)。

没了。不知道为什么有人写线段树。

 

代码:

#include<bits/stdc++.h>
#define maxn 200005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
#define rint register int
#define debug(x) cerr<<#x<<": "<<x<<endl
#define fgx cerr<<"--------------"<<endl
#define dgx cerr<<"=============="<<endl

using namespace std;
struct Point{int x[2],iid;}p[maxn];
int mx[maxn][2],mn[maxn][2],id[maxn],ls[maxn],rs[maxn];
int tot,np,dis[maxn<<1],vis[maxn<<1],tim[maxn];
int L[maxn],R[maxn],D[maxn],U[maxn];
struct node{int u,d;bool operator<(const node &b)const{return d>b.d;}};
priority_queue<node> q; vector<int> vc[maxn];

inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}

inline node md(int u,int d){node res;res.u=u,res.d=d;return res;}
inline void upd(int u,int v){if(dis[u]>v){dis[u]=v,q.push(md(u,v));}}
inline bool ok(int xa,int ya,int xb,int yb){return xb<=xa&&ya<=yb;}
inline bool cmp(Point a,Point b){return (!np)?(a.x[0]<b.x[0]):(a.x[1]<b.x[1]);}
inline void pushup(int k){
    for(rint i=0;i<2;i++){
        mx[k][i]=mn[k][i]=p[id[k]].x[i];
        if(ls[k]) mx[k][i]=max(mx[k][i],mx[ls[k]][i]),mn[k][i]=min(mn[k][i],mn[ls[k]][i]);
        if(rs[k]) mx[k][i]=max(mx[k][i],mx[rs[k]][i]),mn[k][i]=min(mn[k][i],mn[rs[k]][i]);
    }
}
inline int build(int l,int r,int type){
    if(l>r) return 0;
    int mid=l+r>>1,k=++tot; 
    id[k]=mid,np=type;
    nth_element(p+l,p+mid,p+r+1,cmp);
    ls[k]=build(l,mid-1,type^1);
    rs[k]=build(mid+1,r,type^1);
    pushup(k); return k;
}
inline void add(int l,int r,int d,int u,int c,int k){
    if(mx[k][0]<l || mn[k][0]>r || mx[k][1]<d || mn[k][1]>u) return;
    if(ok(mn[k][0],mx[k][0],l,r)&&ok(mn[k][1],mx[k][1],d,u)){upd(k,c);return;}   
    if(ok(p[id[k]].x[0],p[id[k]].x[0],l,r)&&ok(p[id[k]].x[1],p[id[k]].x[1],d,u)) upd(p[id[k]].iid,c);
    if(ls[k]) add(l,r,d,u,c,ls[k]); if(rs[k]) add(l,r,d,u,c,rs[k]);
} 

inline void Dijkstra(int s,int n){
    memset(vis,0,sizeof(vis));
    memset(dis,127,sizeof(dis));
    dis[s]=0,q.push(md(s,0));
    while(!q.empty()){
        rint u=q.top().u; q.pop();
        if(vis[u]) continue; vis[u]=1;
        if(u<=n) for(rint i=0;i<vc[u].size();i++) 
            add(L[vc[u][i]],R[vc[u][i]],D[vc[u][i]],U[vc[u][i]],dis[u]+tim[vc[u][i]],n+1);
        else upd(p[id[u]].iid,dis[u]),upd(ls[u],dis[u]),upd(rs[u],dis[u]);
    }
}

int main(){
    freopen("jump.in","r",stdin);
    freopen("jump.out","w",stdout);
    int n=read(),m=read(),w=read(),h=read();
    for(rint i=1;i<=n;i++) 
        p[i].x[0]=read(),p[i].x[1]=read(),p[i].iid=i;
    tot=n,build(1,n,0);
    for(rint i=1;i<=m;i++){
        int p=read();tim[i]=read();
        L[i]=read(),R[i]=read();
        D[i]=read(),U[i]=read();
        vc[p].push_back(i);
    }
    Dijkstra(1,n);
    for(rint i=2;i<=n;i++) printf("%d\n",dis[i]);
    return 0;
}
D2T1
posted @ 2019-12-10 10:19  Fugtemypt  阅读(281)  评论(0编辑  收藏  举报