【NOI2019】【LOJ3259】【洛谷P5471】弹跳(K-D Tree)(最短路)

LOJ传送门

洛谷传送门


(题解) 社论:

我觉得我这个是个乱搞,但是是个复杂度优秀的乱搞,好像只有 O ( m log ⁡ m + n log ⁡ 2 n ) O(m\log m+n\log^2 n) O(mlogm+nlog2n)

一眼KDTree优化建图跑dijkstra,然后写了个滚动切割静态KD-Tree,边数被卡爆只有88pts。

所以我们的策略是不建边。在优先队列里面保留整个矩形,然后考虑用某种数据结构询问当前矩形内部所有没有访问过的点。

直接上KD-Tree是期望 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23),但是我们发现这个KD-Tree是个静态的,所以考虑滚动切割,按照维度滚动,然后贪心分治,这样建树只有 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n),而且常数极小。

然后我们从优先队列里面拿出来矩形,在KD-Tree上面找就行了,这部分找点的总复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn),常数不超过 4 4 4

加上dijkstra的复杂度也才 O ( m log ⁡ m + n log ⁡ 2 n ) O(m\log m+n\log^2 n) O(mlogm+nlog2n)

拿下LOJ和洛谷速度榜Rk 1。


Update:

怎么总是有人说我复杂度假掉了?

看清楚,这不是任何一种传统的KD-Tree建树方式!

来证明一下:

建树:根据贪心划分(自己去看我是怎么划分的),保证左右儿子的点数严格为父亲点数的一半,单个节点建树需要按照横纵坐标排序 f ( n ) = O ( n log ⁡ n ) f(n)=O(n\log n) f(n)=O(nlogn),复杂度瓶颈就在这里,建树总复杂度 T ( n ) = 2 T ( n 2 ) + f ( n ) = O ( n log ⁡ 2 n ) T(n)=2T(\frac{n}{2})+f(n)=O(n\log^2n) T(n)=2T(2n)+f(n)=O(nlog2n)

查询:首先树高是 O ( log ⁡ n ) O(\log n) O(logn)的,空矩形的访问复杂度最多只有 O ( log ⁡ n ) O(\log n) O(logn)

显然这道题由于是访问一个叶子节点之后需要删除,至少会导致其到根的路径上的点的 s i z − 1 siz-1 siz1。空节点不用再次访问,则复杂度显然为 O ( ∑ s i z ) O(\sum siz) O(siz),而树高为 O ( log ⁡ n ) O(\log n) O(logn),一个叶子显然只会对 O ( log ⁡ n ) O(\log n) O(logn)个节点有贡献,所以总复杂度为 O ( ∑ s i z ) = O ( n log ⁡ n ) O(\sum siz)=O(n\log n) O(siz)=O(nlogn),如果非要用空矩形来卡的话就是 O ( m log ⁡ n ) O(m\log n) O(mlogn)

复杂度和常数都十分优秀。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int getint(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
typedef std::pair<ll,int> pli;
#define fi first
#define se second

cs int N=7e4+4,M=1.5e5+5;

int n,m,w,h;

struct Point{
	int x,y,id;
}p[N];

int nd[N],cnt;
namespace KDT{
	cs int N=::N<<1;
	int lc[N],rc[N],typ[N],now,rt;
	int U[N],D[N],L[N],R[N],siz[N],id[N];
	
	bool cmp1(cs Point &a,cs Point &b){return a.x<b.x;}
	bool cmp2(cs Point &a,cs Point &b){return a.y<b.y;}
	
	void build(int &rt,int l,int r,int u,int d,int pl,int pr,int lasttyp=0){
		rt=++now;
		L[rt]=l,R[rt]=r,D[rt]=d,U[rt]=u;
		siz[rt]=pr-pl+1;
		if(pl==pr){
			id[rt]=p[pl].id;
			return ;
		}
		if(u==d){
			typ[u]=1;
			if(typ[u]!=lasttyp)std::sort(p+pl,p+pr+1,cmp1);
			int mid=pl+pr>>1;
			build(lc[rt],l,p[mid].x,u,u,pl,mid,1);
			build(rc[rt],p[mid+1].x,r,u,u,mid+1,pr,1);
			return ;
		}
		if(l==r){
			typ[u]=2;
			if(typ[u]!=lasttyp)std::sort(p+pl,p+pr+1,cmp2);
			int mid=pl+pr>>1;
			build(lc[rt],l,l,u,p[mid].y,pl,mid,2);
			build(rc[rt],l,l,p[mid+1].y,d,mid+1,pr,2);
			return ;
		}
		typ[u]=3-lasttyp;
		if(typ[u]==3)typ[u]=1;
		if(typ[u]==1){
			std::sort(p+pl,p+pr+1,cmp1);
			int mid=pl+pr>>1;
			int nu=0x3f3f3f3f,nd=0;
			for(int re i=pl;i<=mid;++i)nu=std::min(nu,p[i].y),nd=std::max(nd,p[i].y);
			build(lc[rt],l,p[mid].x,nu,nd,pl,mid,1);
			nu=0x3f3f3f3f,nd=0;
			for(int re i=mid+1;i<=pr;++i)nu=std::min(nu,p[i].y),nd=std::max(nd,p[i].y);
			build(rc[rt],p[mid+1].x,r,nu,nd,mid+1,pr,1);
		}
		else {
			std::sort(p+pl,p+pr+1,cmp2);
			int mid=pl+pr>>1;
			int nl=0x3f3f3f3f,nr=0;
			for(int re i=pl;i<=mid;++i)nl=std::min(nl,p[i].x),nr=std::max(nr,p[i].x);
			build(lc[rt],nl,nr,u,p[mid].y,pl,mid,2);
			nl=0x3f3f3f3f,nr=0;
			for(int re i=mid+1;i<=pr;++i)nl=std::min(nl,p[i].x),nr=std::max(nr,p[i].x);
			build(rc[rt],nl,nr,p[mid+1].y,d,mid+1,pr,2);
		}
	}
	
	void get(int rt,int l,int r,int u,int d){
		if(!siz[rt])return ;
		if(r<L[rt]||R[rt]<l||d<U[rt]||D[rt]<u)return ;
		if(id[rt]){
			siz[rt]=0;
			nd[++cnt]=id[rt];
			return ;
		}
		if(typ[rt]==1){
			if(l<=R[lc[rt]])get(lc[rt],l,r,u,d);
			if(L[rc[rt]]<=r)get(rc[rt],l,r,u,d);
		}
		else {
			if(u<=D[lc[rt]])get(lc[rt],l,r,u,d);
			if(U[rc[rt]]<=d)get(rc[rt],l,r,u,d);
		}
		siz[rt]=siz[lc[rt]]+siz[rc[rt]];
	}
}

struct rac{
	int l,r,u,d;ll t;
	rac(int _l,int _r,int _u,int _d,ll _t):l(_l),r(_r),u(_u),d(_d),t(_t){}
	friend bool operator>(cs rac &a,cs rac &b){
		return a.t>b.t;
	}
};

std::vector<rac> G[N];
std::priority_queue<rac,std::vector<rac>,std::greater<rac> > q;

bool vis[N];
ll dist[N];

inline void Dij(){
	memset(dist,0x3f,sizeof dist);
	dist[1]=0;
	for(rac &t:G[1])q.push(t);
	vis[1]=true;
	while(!q.empty()){
		int l=q.top().l,r=q.top().r,u=q.top().u,d=q.top().d;ll t=q.top().t;
		q.pop();cnt=0;
		KDT::get(KDT::rt,l,r,u,d);
		for(int re i=1;i<=cnt;++i){
			int u=nd[i];if(vis[u])continue;vis[u]=true;
			dist[u]=t;
			for(rac &e:G[u]){
				q.push(rac(e.l,e.r,e.u,e.d,e.t+t));
			}
		}
	}
}

int mnx=0x3f3f3f3f,mxx,mny=0x3f3f3f3f,mxy;
signed main(){
//	freopen("jump.in","r",stdin);freopen("jump.out","w",stdout);
	n=getint(),m=getint(),w=getint(),h=getint();
	for(int re i=1;i<=n;++i){
		p[i].x=getint(),p[i].y=getint(),p[i].id=i;
		mnx=std::min(mnx,p[i].x);
		mxx=std::max(mxx,p[i].x);
		mny=std::min(mny,p[i].y);
		mxy=std::max(mxy,p[i].y);
	}
	KDT::build(KDT::rt,mnx,mxx,mny,mxy,1,n);
	for(int re i=1;i<=m;++i){
		int u=getint(),w=getint(),L=getint(),R=getint(),U=getint(),D=getint();
		G[u].push_back(rac(L,R,U,D,w));
	}
	Dij();
	for(int re i=2;i<=n;++i)cout<<dist[i]<<"\n";
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值