[NOI2019]弹跳(KD-Tree/四分树/线段树套平衡树 优化建图+Dijkstra)
本题可以用的方法很多,除去以下三种我所知道的就还有至少三种。
方法一:类似线段树优化建图,将一个平面等分成四份(若只有一行或一列则等分成两份),然后跑Dijkstra即可。建树是$O(n\log n)$的,单次连边是$O(n\log^2 n)$的。
1 #include<queue> 2 #include<vector> 3 #include<cstdio> 4 #include<cstring> 5 #include<algorithm> 6 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 7 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 8 using namespace std; 9 10 const int N=1000010,M=1500010; 11 struct E{ int w,l,r,u,d; }p[N]; 12 struct P{ int u,d; }; 13 vector<int>G[N]; 14 int n,m,W,H,now,dis,x,y,cnt,rt,tot,h[N],to[M],nxt[M],w[M],vis[N],d[N],ch[N][4]; 15 bool operator <(P x,P y){ return x.d>y.d; } 16 priority_queue<P>Q; 17 void add(int x,int y,int z){ to[++cnt]=y; nxt[cnt]=h[x]; w[cnt]=z; h[x]=cnt; } 18 19 void ins(int fa,int &k,int xl,int xr,int yl,int yr,int x,int y){ 20 if (x<xl||x>xr||y<yl||y>yr) return; 21 if (!k) k=++tot; 22 if (k!=rt) add(fa+n,k+n,0); 23 if (xl==xr&&yl==yr){ add(k+n,now,0); return; } 24 int xm=(xl+xr)>>1,ym=(yl+yr)>>1; 25 ins(k,ch[k][0],xl,xm,yl,ym,x,y); 26 ins(k,ch[k][1],xl,xm,ym+1,yr,x,y); 27 ins(k,ch[k][2],xm+1,xr,yl,ym,x,y); 28 ins(k,ch[k][3],xm+1,xr,ym+1,yr,x,y); 29 } 30 31 void link(int k,int xl,int xr,int yl,int yr,int xL,int xR,int yL,int yR){ 32 if (!k||xR<xl||xL>xr||yR<yl||yL>yr||d[k+n]<=dis) return; 33 if (xl>=xL&&xr<=xR&&yl>=yL&&yr<=yR){ d[k+n]=dis; Q.push((P){k+n,d[k+n]}); return; } 34 int xm=(xl+xr)>>1,ym=(yl+yr)>>1; 35 link(ch[k][0],xl,xm,yl,ym,xL,xR,yL,yR); 36 link(ch[k][1],xl,xm,ym+1,yr,xL,xR,yL,yR); 37 link(ch[k][2],xm+1,xr,yl,ym,xL,xR,yL,yR); 38 link(ch[k][3],xm+1,xr,ym+1,yr,xL,xR,yL,yR); 39 } 40 41 int main(){ 42 freopen("jump.in","r",stdin); 43 freopen("jump.out","w",stdout); 44 scanf("%d%d%d%d",&n,&m,&W,&H); 45 rep(i,1,n) scanf("%d%d",&x,&y),now=i,ins(0,rt,1,W,1,H,x,y); 46 rep(i,1,m) scanf("%d%d%d%d%d%d",&now,&p[i].w,&p[i].l,&p[i].r,&p[i].u,&p[i].d),G[now].push_back(i); 47 memset(d,63,sizeof(d)); d[1]=0; Q.push((P){1,0}); 48 while (!Q.empty()){ 49 int u=Q.top().u; Q.pop(); 50 if (vis[u]) continue; vis[u]=1; 51 for (int i=0;i<(int)G[u].size();i++) 52 x=G[u][i],dis=d[u]+p[x].w,link(rt,1,W,1,H,p[x].l,p[x].r,p[x].u,p[x].d); 53 For(i,u) if (d[k=to[i]]>d[u]+w[i]) Q.push((P){k,d[k]=d[u]+w[i]}); 54 } 55 rep(i,2,n) printf("%d\n",d[i]); 56 return 0; 57 }
方法二:一维使用线段树,另一维用set维护这行中的每个点。注意到在Dijikstra时,一条边至多被松弛一次,即一个矩阵至多被访问一次,所以访问并更新一个点之后就可以直接将其删去,复杂度$O(n\log^2 n+m\log m)$。
1 #include<set> 2 #include<queue> 3 #include<vector> 4 #include<cstdio> 5 #include<algorithm> 6 #define ls (x<<1) 7 #define rs (ls|1) 8 #define lson ls,l,mid 9 #define rson rs,mid+1,r 10 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 11 using namespace std; 12 typedef pair<int, int>pii; 13 typedef multiset<pii>::iterator iter; 14 15 const int N=70010,M=150010,S=(1<<18)+10; 16 int n,m,W,H,x,p,id,yp[N],vis[N],dis[N],h[N],nxt[M],val[M],L[M],R[M],D[M],U[M]; 17 multiset<pii>st[S]; 18 priority_queue<pii> Q; 19 20 void ins(int x,int l,int r,int k,int id){ 21 st[x].insert(pii(yp[id],id)); 22 if (l==r) return; 23 int mid=(l+r)>>1; 24 if (k<=mid) ins(lson,k,id); else ins(rson,k,id); 25 } 26 27 void del(int x,int l,int r,int id,int d){ 28 if (r<L[id] || R[id]<l) return; 29 if (L[id]<=l && r<=R[id]){ 30 iter it=st[x].lower_bound(pii(D[id],0)),tmp; 31 while (it!=st[x].end() && it->first<=U[id]){ 32 int u=it->second; 33 if (!vis[u]){ 34 vis[u]=1; dis[u]=d; 35 for (int j=h[u]; j; j=nxt[j]) Q.push(pii(-d-val[j],j)); 36 } 37 tmp=it; it++; st[x].erase(tmp); 38 } 39 return; 40 } 41 int mid=(l+r)>>1; del(lson,id,d); del(rson,id,d); 42 } 43 44 int main(){ 45 freopen("jump.in","r",stdin); 46 freopen("jump.out","w",stdout); 47 scanf("%d%d%d%d",&n,&m,&W,&H); 48 rep(i,1,n) scanf("%d%d",&x,&yp[i]),ins(1,1,W,x,i); 49 rep(i,1,m) scanf("%d%d%d%d%d%d",&p,&val[i],&L[i],&R[i],&D[i],&U[i]),nxt[i]=h[p],h[p]=i; 50 dis[1]=0; vis[1]=1; 51 for (int i=h[1]; i; i=nxt[i]) Q.push(pii(-val[i],i)); 52 while (!Q.empty()){ 53 pii ed=Q.top(); Q.pop(); 54 int dis=-ed.first; id=ed.second; del(1,1,W,id,dis); 55 } 56 rep(i,2,n) printf("%d\n",dis[i]); 57 return 0; 58 }
方法三:类似方法二地,用KD-Tree优化建图,树上每个点新建一个新点,每个叶子连向这个坐标对应的点。同样在Dijkstra时,每次访问并更新时删除这个点。复杂度可能可以用一些做到$O(n\log^2 n)$,但这里没用。
1 #include<set> 2 #include<queue> 3 #include<cstdio> 4 #include<vector> 5 #include<cstring> 6 #include<algorithm> 7 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 8 using namespace std; 9 10 const int N=70010,inf=0x3f3f3f3f; 11 int n,m,w,h,rt,D,cnt,id[N*2],dis[N*2],vis[N*2]; 12 int nu,nt,xl,xr,yl,yr,nd[N*2],len; 13 14 struct data{ 15 int nt,xl,xr,yl,yr; 16 bool operator>(const data &b)const{ return nt>b.nt; } 17 }; 18 19 vector<data>f[N*2]; 20 priority_queue<data,vector<data>,greater<data> >Q; 21 22 struct P{ 23 int x[2],id; 24 bool operator<(const P&b)const{ return x[D]!=b.x[D] ? x[D]<b.x[D] : x[D^1]<b.x[D^1]; } 25 }p[N]; 26 27 struct Tr{ int mx[2],mn[2],lc,rc,siz; }t[N*2]; 28 29 void upd(int u){ 30 t[u].mn[0]=t[u].mn[1]=inf; 31 t[u].mx[0]=t[u].mx[1]=-inf; 32 t[u].siz=1; int v=t[u].lc; 33 if (v){ 34 t[u].siz+=t[v].siz; 35 t[u].mx[0]=max(t[u].mx[0],t[v].mx[0]),t[u].mx[1]=max(t[u].mx[1],t[v].mx[1]); 36 t[u].mn[0]=min(t[u].mn[0],t[v].mn[0]),t[u].mn[1]=min(t[u].mn[1],t[v].mn[1]); 37 } 38 v=t[u].rc; 39 if (v){ 40 t[u].siz+=t[v].siz; 41 t[u].mx[0]=max(t[u].mx[0],t[v].mx[0]),t[u].mx[1]=max(t[u].mx[1],t[v].mx[1]); 42 t[u].mn[0]=min(t[u].mn[0],t[v].mn[0]),t[u].mn[1]=min(t[u].mn[1],t[v].mn[1]); 43 } 44 } 45 46 int bud(int l,int r,int wd){ 47 int u=++cnt,mid=(l+r)>>1;t[u].siz=1; 48 if (l==r){ 49 id[u]=p[l].id; 50 t[u].mn[0]=t[u].mx[0]=p[l].x[0]; 51 t[u].mn[1]=t[u].mx[1]=p[l].x[1]; 52 return u; 53 } 54 bool flag=1; 55 rep(i,l+1,r) if(p[i].x[wd]!=p[i-1].x[wd])flag=0; 56 if (flag) wd^=1; 57 D=wd; nth_element(p+l,p+mid,p+1+r); 58 t[u].lc=bud(l,mid,wd^1); t[u].rc=bud(mid+1,r,wd^1); 59 upd(u); return u; 60 } 61 62 void dfs(int u){ 63 if (!t[u].siz)return; 64 if (t[u].mn[0]>xr||t[u].mx[0]<xl||t[u].mn[1]>yr||t[u].mx[1]<yl)return; 65 if (id[u]){ nd[++len]=id[u]; t[u].siz=0; return; } 66 dfs(t[u].lc); dfs(t[u].rc); 67 t[u].siz=t[t[u].lc].siz+t[t[u].rc].siz; 68 } 69 70 int main(){ 71 freopen("jump.in","r",stdin); 72 freopen("jump.out","w",stdout); 73 memset(dis,63,sizeof(dis)); 74 scanf("%d%d%d%d",&n,&m,&w,&h); 75 rep(i,1,n) scanf("%d%d",&p[i].x[0],&p[i].x[1]),p[i].id=i; 76 rt=bud(1,n,0); 77 rep(i,1,m){ 78 scanf("%d%d%d%d%d%d",&nu,&nt,&xl,&xr,&yl,&yr); 79 f[nu].push_back((data){nt,xl,xr,yl,yr}); 80 } 81 dis[1]=0; vis[1]=1; 82 for(int i=0; i<(int)f[1].size(); i++) Q.push(f[1][i]); 83 while (!Q.empty()){ 84 nt=Q.top().nt,xl=Q.top().xl,xr=Q.top().xr,yl=Q.top().yl,yr=Q.top().yr; 85 Q.pop(); len=0; dfs(rt); 86 rep(i,1,len){ 87 int u=nd[i]; 88 if (vis[u])continue; vis[u]=1; 89 dis[u]=nt; 90 for (int j=0; j<(int)f[u].size(); ++j){ 91 data tp=f[u][j]; tp.nt+=nt; Q.push(tp); 92 } 93 } 94 } 95 rep(i,2,n) printf("%d\n",dis[i]); 96 return 0; 97 }