【NOI2019】弹跳【二维线段树】【dijkstra】

题意:一个 w × h w\times h w×h的二维平面上有 n n n个城市,有 m m m个弹跳装置,第 i i i个可以花费 t i t_i ti的时间从城市 p i p_i pi跳到矩形 x ∈ [ l , r ] , y ∈ [ u , d ] x\in [l,r],y\in[u,d] x[l,r],y[u,d]中的任意一个城市。求从 1 1 1到其他每个城市的最小时间。

w , h ≤ n ≤ 7 × 1 0 4 , m ≤ 1.5 × 1 0 5 w,h\leq n\leq7\times 10^4,m\leq1.5\times10^5 w,hn7×104,m1.5×105 空间限制128M

考虑直接套dijkstra的思路,每次选出dis最小的点,松弛它可以到的点,然后把它删掉。

挪到二维平面上,你需要支持:

  1. 全局询问最小值
  2. 矩形取 min ⁡ \min min
  3. 删除一个点

用kdt或者二维线段树维护即可。这里用的是二维线段树。

需要注意的细节:

  1. 要动态开点
  2. 要用pair记录最小值的编号,且初值是(INF,INF),否则可能打了个lazy标记之后first是答案标号却是0。
  3. 大常数选手需要维护一个最大值剪枝。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <vector>
#include <utility>
#define re register
#define MAXN 70005
using namespace std;
const int INF=0x7fffffff;
inline int read()
{
	int ans=0;
	char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
typedef pair<int,int> pi;
#define mp make_pair
#define fi first
#define se second
struct tran{int xl,xr,yl,yr;tran(int xl=0,int xr=0,int yl=0,int yr=0):xl(xl),xr(xr),yl(yl),yr(yr){}};
inline bool belong(const tran& b,const tran& a){return a.xl<=b.xl&&b.xr<=a.xr&&a.yl<=b.yl&&b.yr<=a.yr;}
inline bool ninter(const tran& a,const tran& b){return (a.xr<b.xl||b.xr<a.xl)||(a.yr<b.yl||b.yr<a.yl);}
struct edge{tran t;int w;};
vector<edge> e[MAXN];
#define ch(x) t[p].son[x]
int tot=0,rt;
struct node{tran pos;pi mn;int mx,sum,lzy,son[4];}t[MAXN<<5];
inline int newnode(int xl,int xr,int yl,int yr)
{
	int p=++tot;
	t[p].pos=tran(xl,xr,yl,yr);
	t[p].mn=mp(INF,INF);
	t[p].mx=0;
	t[p].lzy=INF;
	return p;
}
inline void update(int p)
{
	t[p].sum=t[ch(0)].sum+t[ch(1)].sum+t[ch(2)].sum+t[ch(3)].sum;
	t[p].mn=mp(INF,INF),t[p].mx=0;
	for (re int i=0;i<4;++i) 
		if (ch(i)) 
			t[p].mn=min(t[p].mn,t[ch(i)].mn),t[p].mx=max(t[p].mx,t[ch(i)].mx);
}
inline void pushlzy(int p,int v)
{
	if (t[p].sum==0) return;
	t[p].mn.fi=min(t[p].mn.fi,v),t[p].mx=min(t[p].mx,v),t[p].lzy=min(t[p].lzy,v);
}
inline void pushdown(int p)
{
	if (t[p].lzy<INF)
	{
		for (re int i=0;i<4;++i) pushlzy(ch(i),t[p].lzy);
		t[p].lzy=INF;
	}
}
void modify(int& p,int xl,int xr,int yl,int yr,int x,int y,int v)
{
	if (!p) p=newnode(xl,xr,yl,yr);
	if (!belong(tran(x,x,y,y),t[p].pos)) return;
	pushdown(p);
	if (t[p].pos.xl==t[p].pos.xr&&t[p].pos.yl==t[p].pos.yr) 
		return (void)(t[p].sum=(v<INF),t[p].mn=mp(INF,v),t[p].mx=(v==INF? 0:INF));
	int xmid=(xl+xr)>>1,ymid=(yl+yr)>>1;	
	modify(ch(0),xl,xmid,yl,ymid,x,y,v);
	modify(ch(1),xl,xmid,ymid+1,yr,x,y,v);
	modify(ch(2),xmid+1,xr,yl,ymid,x,y,v);
	modify(ch(3),xmid+1,xr,ymid+1,yr,x,y,v);		
	update(p);
}
void modify(int p,tran q,int v)
{
	if (!p) return;
	pushdown(p);	
	if (t[p].mx<=v) return;
	if (belong(t[p].pos,q)) return pushlzy(p,v);
	if (ninter(t[p].pos,q)) return;
	for (int i=0;i<4;i++) 
		modify(ch(i),q,v);
	update(p);	
}
int x[MAXN],y[MAXN],ans[MAXN];
int main()
{
	int n,m,w,h;
	n=read(),m=read(),w=read(),h=read();
	for (int i=1;i<=n;i++)
	{
		x[i]=read(),y[i]=read();
		modify(rt,1,w,1,h,x[i],y[i],i);
	}
	for (int i=1;i<=m;i++)
	{
		int p,t,l,r,d,u;
		p=read(),t=read(),l=read(),r=read(),d=read(),u=read();
		e[p].push_back((edge){tran(l,r,d,u),t});
	}
	modify(1,tran(x[1],x[1],y[1],y[1]),0);
	for (int T=1;T<=n;T++)
	{
		pi tmp=t[1].mn;
		int u=tmp.se,dis=tmp.fi;
		ans[u]=dis;
		for (int i=0;i<(int)e[u].size();i++)
			modify(1,e[u][i].t,dis+e[u][i].w);
		modify(rt,1,w,1,h,x[u],y[u],INF);
	}
	for (int i=2;i<=n;i++) printf("%d\n",ans[i]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值