[NOI2019]弹跳 [四分树+dijsktra]

传送门

打模拟赛只会线段树优化建图,于是只有72分

后来问 ldx 神仙学长,他说是个叫四分树的东西,我一脸懵逼?

他说线段树是一分为2,四方树是把一个矩阵一分为4

我想了想,好有道理啊!于是开始自己 yy

考虑动态开点,把每个点插入到树中,类似线段树,只会多log个节点

但优化建边的时候,每条边会想树上连log^2 个点,显然要凉

于是我想,dijsktra的时候在矩阵里面直接查然后更新最短路就可以了,不需要记录边

另外四方树更线段树一样啊,自己瞎写一下居然对了,真是个毒瘤的数据结构 !


#include<bits/stdc++.h>
#define N 3000050
#define M 70050
using namespace std;

int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-'0'), ch = getchar();
	return cnt * f;
}

typedef long long ll;
int n, m, W, H;
struct Node{ int p, t, L, R, D, U;}; 
vector<Node> v[M];

int rt, ch[N][4], res, pos[N], pre[N];
void Insert(int &u, int lx, int rx, int ly, int ry, int x, int y, int id){
	if(!u) u = ++res;
	if(lx == rx && ly == ry){ pos[id] = u; pre[u] = id; return; }
	int midx = (lx + rx) >> 1, midy = (ly + ry) >> 1;
	if(x <= midx){
		if(y <= midy) Insert(ch[u][0], lx, midx, ly, midy, x, y, id);
		else Insert(ch[u][1], lx, midx, midy+1, ry, x, y, id);
	}
	else{
		if(y <= midy) Insert(ch[u][2], midx+1, rx, ly, midy, x, y, id);
		else Insert(ch[u][3], midx+1, rx, midy+1, ry, x, y, id);
	} 
}

#define mp make_pair
ll dis[N]; bool vis[N];
priority_queue<pair<ll, int> > q;
void Modify(int u, int lx, int rx, int ly, int ry, ll d, Node v){
	if(!u) return; if(dis[u] <= d + v.t) return;
	if(v.L <= lx && rx <= v.R && v.D <= ly && ry <= v.U){
		if(d + v.t < dis[u]){
			dis[u] = d + v.t;
			q.push(mp(-dis[u], u));
		} return;
	} int midx = (lx + rx) >> 1, midy = (ly + ry) >> 1;
	if(ch[u][0] && v.L <= midx && v.D <= midy) Modify(ch[u][0], lx, midx, ly, midy, d, v);
	if(ch[u][1] && v.L <= midx && v.U > midy) Modify(ch[u][1], lx, midx, midy+1, ry, d, v);
	if(ch[u][2] && v.R > midx && v.D <= midy) Modify(ch[u][2], midx+1, rx, ly, midy, d, v);
	if(ch[u][3] && v.R > midx && v.U > midy) Modify(ch[u][3], midx+1, rx, midy+1, ry, d, v); 
}

int main(){
	freopen("jump.in","r",stdin);
	freopen("jump.out","w",stdout);
	n = read(), m = read(), W = read(), H = read(); res = n;
	for(int i=1; i<=n; i++){
		int x = read(), y = read();
		Insert(rt, 1, W, 1, H, x, y, i);
	} 
	for(int i=1; i<=m; i++){
		int p = read(), t = read(), L = read(), R = read(), D = read(), U = read();
		v[p].push_back((Node){p, t, L, R, D, U});
	}
		
	memset(dis, 127, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	dis[pos[1]] = 0; q.push(mp(0, pos[1])); 
	while(!q.empty()){
		int x = q.top().second; q.pop();
		if(vis[x]) continue; vis[x] = true;
		if(pre[x]){
			for(int i=0; i<v[pre[x]].size(); i++){
				Node now = v[pre[x]][i];
				Modify(rt, 1, W, 1, H, dis[x], now);
			}
		}
		else{
			for(int j = 0; j < 4; j++){
				if(ch[x][j] && dis[ch[x][j]] > dis[x]){
					dis[ch[x][j]] = dis[x]; q.push(mp(-dis[ch[x][j]], ch[x][j]));
				}
			}
		}
	} 
	for(int i=2; i<=n; i++) printf("%lld\n", dis[pos[i]]);
	return 0;
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值