洛谷 P5471 - [NOI2019] 弹跳(二维线段树优化建图+堆优化存边)

题面传送门

一道非常有意思的题(大概可以这么形容?)

首先看到这类一个点想一个区域内连边的题目可以很自然地想到线段树优化建图,只不过这道题是二维的,因此需要使用二维线段树优化建图,具体来说,我们外层开一棵大线段树维护 \(x\) 轴下标区间,大线段树上每个节点又套了个小的动态开点线段树,每次我们从一个点向一个矩形连边时就在动态开点线段树上找到对应的区间并从这个点向这些区间中连边,不难发现这个做法点数是 \(\mathcal O(n\log^2n)\) 级别的,边数是 \(\mathcal O(m\log^2n)\) 级别的,总复杂度 \(m\log^3n\),然鹅梦想很美满,现实很骨感,这个做法,它,MLE 了!因此我们还得考虑别的做法。

这时候就要用到一个叫做”堆优化存边“的 trick 了,在正常的 dijkstra 求最短路的过程中,我们小根堆中存的是起点到每个点的距离与其形成的 pair,但是这次咱们偏不存点,咱们存,也就是题目中所说的弹跳装置。显然对于一个弹跳装置而言,如果起点到它的出发点 \(p_i\) 的最短路径长度已知,那么它用来更新矩形中所有点的距离就已经知道了——是 \(dis_{p_i}+t_i\),那么我们就将 \(dis_{p_i}+t_i\) 与弹跳装置的编号 \(i\) 看作一个 pair 压入小根堆中,每次取出小根堆的最小节点并找到它对应弹跳装置中的所有节点集合 \(S\)——这个咱们可以用线段树套 set 来求出,然后直接令 \(S\) 当中的点的最短路径为 \(dis_{p_i}+t_i\),然后将这些点直接从线段树中删除,显然每个点最多被访问并删除一次,每个点最多在 \(\log n\) 个节点中出现,再加上 set 的复杂度,总复杂度俩 \(\log\)。而且由于我们每次取出的是贡献最小的弹跳装置,因此每次找出的 \(S\) 中的节点必然是无法被更小的弹跳装置更新的,因此求出来的距离必定是起点到每个点的最短路。这样时间复杂度 \(n\log^2n+m\),空间复杂度 \(n\log n+m\),就不用再担心空间的问题了。

const int MAXN=7e4;
const int MAXM=1.5e5;
int n,m,w,h,dis[MAXN+5];
struct city{
	int x,y,id;
	city(int _x=0,int _y=0,int _id=0):x(_x),y(_y),id(_id){}
	bool operator <(const city &rhs) const{
		return (y^rhs.y)?(y<rhs.y):(id<rhs.id);
	}
} a[MAXN+5];
priority_queue<pii,vector<pii>,greater<pii> > q;
set<city> st[MAXN*4+5];
vector<int> fr[MAXN+5];
struct bar{int p,l,r,u,d,c;} b[MAXM+5];
void insert(int k,int l,int r,int v){
	st[k].insert(a[v]);if(l==r) return;int mid=l+r>>1;
	(a[v].x<=mid)?insert(k<<1,l,mid,v):insert(k<<1|1,mid+1,r,v);
}
void del(int k,int l,int r,int v){
	st[k].erase(st[k].find(a[v]));if(l==r) return;int mid=l+r>>1;
	(a[v].x<=mid)?del(k<<1,l,mid,v):del(k<<1|1,mid+1,r,v);
}
void update(int k,int l,int r,int x,int y){
	if(b[x].l<=l&&r<=b[x].r){
		while(1){
			set<city>::iterator it=st[k].lower_bound(city(0,b[x].d,0));
			if(it==st[k].end()||(it->y)>b[x].u) break;
			int id=(it->id);dis[id]=y;
			for(int t:fr[id]) q.push(mp(y+b[t].c,t));
			del(1,1,w,id);
		} return;
	} int mid=l+r>>1;
	if(b[x].r<=mid) update(k<<1,l,mid,x,y);
	else if(b[x].l>mid) update(k<<1|1,mid+1,r,x,y);
	else update(k<<1,l,mid,x,y),update(k<<1|1,mid+1,r,x,y);
}
int main(){
	scanf("%d%d%d%d",&n,&m,&w,&h);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y),a[i].id=i,insert(1,1,w,i);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d%d%d%d",&b[i].p,&b[i].c,&b[i].l,&b[i].r,&b[i].d,&b[i].u);
		fr[b[i].p].pb(i);
	} b[++m].c=0;b[m].l=b[m].r=a[1].x;b[m].u=b[m].d=a[1].y;q.push(mp(0,m));
	while(!q.empty()){pii p=q.top();q.pop();/*printf("%d %d\n",p.fi,p.se);*/update(1,1,w,p.se,p.fi);}
	for(int i=2;i<=n;i++) printf("%d\n",dis[i]);
	return 0;
}
posted @ 2021-06-29 23:26  tzc_wk  阅读(53)  评论(0编辑  收藏  举报