desciption
有个城市,所有城市在的网格图上,不同城市坐标不同。
有个弹跳机,号弹跳机可以从城市到,的城市
问从城市1出发到所有点的最短路。
solution
kd-tree优化建图
先用城市点建kd-tree
每个kd-tree上的点管辖范围都是一个矩形
对于每个点,枚举以它为出发点的弹跳机,并在kd-tree上查找该弹跳机能到达的矩形的子矩形连边。
具体:
(kd-tree上节点)
1.判断弹跳机到达的矩形是否包含该节点的矩形(是就向该节点连边return)
2.判断该节点本身的城市是否在弹跳机矩形内(是就连边)。
3.到达左右两个节点(分别判断前提:是否存在交集)
不过这样做空间会爆
实际上并不需要连实边。
你Dijskra()到点.直接再在kd-tree上临时找边即可。
具体:
1.:松弛本身城市节点和左右儿子
2.:枚举以出发的弹跳机,在kd-tree上找边(类上面,不说了)。
code
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,W,H,nxt[N],to[N],head[N],ecnt,idx;
int dis[N];
struct city {
int z[2],id;
bool operator<(const city &u) const{return z[idx]<u.z[idx];}
}a[N];
char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
inline int read() {
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
return x*f;
}
struct jump {
int p,t,l[2],r[2];
}J[N];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
struct node {int mid,l[2],r[2];}T[N];
int ls[N],rs[N];
struct pq {
int p,w;
bool operator<(const pq &u) const{return w>u.w;}
};
priority_queue<pq> Q;
void Update(int y,int z) {
if(dis[y]>z) {Q.push((pq){y,dis[y]=z});}
}
struct kd_tree {
int nd;
void Build(int x,int l,int r) {
int _idx(idx);idx^=1;int dx(idx);
int mid=(l+r)>>1;
nth_element(a+l,a+mid,a+r+1);
T[x].mid=mid;
T[x].l[0]=T[x].r[0]=a[mid].z[0];T[x].l[1]=T[x].r[1]=a[mid].z[1];
if(l!=mid) {
ls[x]=++nd;
Build(nd,l,mid-1);idx=dx;int L=ls[x];
T[x].l[0]=min(T[x].l[0],T[L].l[0]);T[x].l[1]=min(T[x].l[1],T[L].l[1]);
T[x].r[0]=max(T[x].r[0],T[L].r[0]);T[x].r[1]=max(T[x].r[1],T[L].r[1]);
}
if(mid!=r) {
rs[x]=++nd;
Build(nd,mid+1,r);int R=rs[x];
T[x].l[0]=min(T[x].l[0],T[R].l[0]);T[x].l[1]=min(T[x].l[1],T[R].l[1]);
T[x].r[0]=max(T[x].r[0],T[R].r[0]);T[x].r[1]=max(T[x].r[1],T[R].r[1]);
}
}
void Mk_tree() {
nd=n+1;
T[nd].l[0]=1e9,T[nd].r[0]=0;T[nd].l[1]=1e9;T[nd].r[1]=0;
for(int i=1;i<=n;i++) {
T[nd].l[0]=min(T[nd].l[0],a[i].z[0]);T[nd].r[0]=max(T[nd].r[0],a[i].z[0]);
T[nd].l[1]=min(T[nd].l[1],a[i].z[1]);T[nd].r[1]=max(T[nd].r[1],a[i].z[1]);
}
Build(nd,1,n);
}
void Go(int x,int id,int w) { //id(jump)
idx^=1;int dx(idx);
if(dis[x]<=w) return;
if(T[x].l[0]>=J[id].l[0]&&T[x].l[1]>=J[id].l[1]&&T[x].r[0]<=J[id].r[0]&&T[x].r[1]<=J[id].r[1]) {Update(x,w);return;}
int mid=T[x].mid,L(ls[x]),R(rs[x]),pos(a[mid].id);
if(J[id].l[0]<=a[mid].z[0]&&J[id].l[1]<=a[mid].z[1]&&J[id].r[0]>=a[mid].z[0]&&J[id].r[1]>=a[mid].z[1]) {Update(pos,w);}
if(L&&J[id].l[dx]<=T[L].r[dx])Go(L,id,w),idx=dx;
if(R&&J[id].r[dx]>=T[R].l[dx])Go(R,id,w);
}
}KD;
void DJ(int s) {
memset(dis,0x3f,sizeof(dis));
Q.push((pq){s,0});dis[s]=0;
while(!Q.empty()) {
if(dis[Q.top().p]<Q.top().w){Q.pop();continue;} //There often be many same node in it(pop just leave the smallest one)
int u=Q.top().p;Q.pop();
if(u>n) {
if(ls[u]) {Update(ls[u],dis[u]);}
if(rs[u]) {Update(rs[u],dis[u]);}
Update(a[T[u].mid].id,dis[u]);
}
else {
for(int i=head[u];i;i=nxt[i]) {
int v=to[i];
idx=0;KD.Go(n+1,v,dis[u]+J[v].t);
}
}
}
}
int main() {
// freopen("data.in","r",stdin);
// freopen("ans2.out","w",stdout);
// scanf("%d%d%d%d",&n,&m,&W,&H);
n=read(),m=read(),W=read(),H=read();
// printf("!%d\n",n);
for(int i=1;i<=n;i++) {a[i].z[0]=read();a[i].z[1]=read();a[i].id=i;}
KD.Mk_tree();
for(int i=1;i<=m;i++) {
J[i].p=read();J[i].t=read();J[i].l[0]=read();J[i].r[0]=read();J[i].l[1]=read();J[i].r[1]=read();
// scanf("%d%d%d%d%d%d",&J[i].p,&J[i].t,&J[i].l[0],&J[i].r[0],&J[i].l[1],&J[i].r[1]);
add_edge(J[i].p,i);
}
DJ(1);
for(int i=2;i<=n;i++) printf("%d\n",dis[i]);
return 0;
}
qaq
- 还是卡常数luogu刚好卡过。可nkoj当然过不了啦。
因此我改了两节课,试图看我跟题解写的有什么区别(实际题解跑得也不是很快)
后来从讨论版学到了:在kd_tree上点时,判断包含关系前,如果已经小于我们要松弛的长度了。和的子树都不用去了(因为kd-tree上时间边权为,能到u的点都能以相同的到的子树,而能到的子树的不一定能到,所以)
于是加了这个剪枝我从倒数刚好卡过的解变成了rk2
太离谱了,可能大部分人都没有看帖子吧。 - 还有一个我常错的问题,注意递归函数全局变量的回溯。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人