【KD-tree】[NOI2019]弹跳

题目

题目描述
跳蚤国有 nn 座城市,分别编号为 1 - n1−n,11 号城市为首都。所有城市分布在一个w \times hw×h 范围的网格上。每座城市都有一个整数坐标 (x, y) (1 \leq x \leq w, 1 \leq y \leq h)(x,y)(1≤x≤w,1≤y≤h),不同城市的坐标不相同。

在跳蚤国中共有 mm 个弹跳装置,分别编号为 1 - m1−m,其中 ii 号弹跳装置位于 p_ip
i

号城市,并具有参数 t_i, L_i, R_i, D_i, U_it
i

,L
i

,R
i

,D
i

,U
i

。利用该弹跳装置,跳蚤可花费 t_i (t_i > 0)t
i

(t
i

  1. 个单位时间,从 p_ip
    i

    号城市跳至坐标满足 L_i \leq x \leq R_i, D_i \leq y \leq U_i (1 \leq L_i \leq R_i \leq w, 1 \leq D_i \leq U_i \leq h)L
    i

    ≤x≤R
    i

    ,D
    i

    ≤y≤U
    i

    (1≤L
    i

    ≤R
    i

    ≤w,1≤D
    i

    ≤U
    i

    ≤h) 的任意一座城市。需要注意的是,一座城市中可能存在多个弹跳装置,也可能没有弹跳装置。

由于城市间距离较远,跳蚤们必须依靠弹跳装置出行。具体来说,一次出行将经过 若干座城市,依次经过的城市的编号可用序列 a_0, a_1, \cdots , a_ka
0

,a
1

,⋯,a
k

表示;在此次出行中,依次利用的弹跳装置的编号可用序列 b_1, b_2, \cdots , b_kb
1

,b
2

,⋯,b
k

表示。其中每座城市可在序列 {a_j}{a
j

} 中出现任意次,每个弹跳装置也可在序列 {b_j}{b
j

} 中出现任意次,且满足,对于每个 j (1 \leq j \leq k)j(1≤j≤k),编号为 b_jb
j

的弹跳装置位于城市 a_{j-1}a
j−1

,且跳蚤能通过该弹跳装置跳至城市 a_ja
j

。我们称这是一次从城市 a_0a
0

到城市 a_ka
k

的出行,其进行了 kk 次弹跳,共花费 \sum^k_{i=1} t_{b_{i}}∑
i=1
k

t
b
i


个单位时间。

现在跳蚤国王想知道,对于跳蚤国除首都(11 号城市)外的每座城市,从首都出发,到达该城市最少需要花费的单位时间。跳蚤国王保证,对每座城市,均存在从首都到它的出行方案。

输入格式
第一行包含四个整数 n, m,w, hn,m,w,h,变量的具体意义见题目描述。

接下来 nn 行,第 ii 行包含两个整数 x_i, y_ix
i

,y
i

,表示 ii 号城市的坐标。

接下来 mm 行,第 ii 行包含六个整数 p_i, t_i, L_i, R_i, D_i, U_ip
i

,t
i

,L
i

,R
i

,D
i

,U
i

,分别表示 ii 号弹跳装置所在的城市编号、弹跳所需的时间、可到达的矩形范围。这些整数的具体意义见题目描述。

输出格式
输出 n - 1n−1 行,第 ii 行包含一个整数,表示从跳蚤国首都到 i + 1i+1 号城市最少需要花费的单位时间。

输入输出样例
输入 #1复制
5 3 5 5
1 1
3 1
4 1
2 2
3 3
1 123 1 5 1 5
1 50 1 5 1 1
3 10 2 2 2 2
输出 #1复制
50
50
60
123
说明/提示
对于所有测试点和样例满足:

1 \leq n \leq 70000 , 1 \leq m \leq 150000 , 1 \leq w, h \leq n , 1 \leq t_i \leq 100001≤n≤70000,1≤m≤150000,1≤w,h≤n,1≤t
i

≤10000。

每个测试点的具体限制见下表。

测试点编号 1\le n\le1≤n≤ 1\le m\le1≤m≤ 特殊限制
1\sim81∼8 100100 100100 无
9\sim139∼13 5\times 10^45×10
4
10^510
5
每个弹跳装置恰好可达一座城市,且 L_i=R_iL
i

=R
i

,D_i=U_iD
i

=U
i

14\sim1814∼18 5\times 10^45×10
4
10^510
5
h=1h=1
19\sim2219∼22 2.5\times 10^42.5×10
4
5\times 10^45×10
4

23\sim2523∼25 7\times 10^47×10
4
1.5\times 10^51.5×10
5

思路

考虑dijkstra的过程:维护一个点集,每一次取出点集里到源点最近的点,用它去更新其他点到源点的距离,然后删除这个点。

那么我们就相当于要维护一个二维棋盘(格子上就是它到源点的距离),并支持以下的操作:

查询全局最小的点

更新子矩阵里所有格子上的值

删除某一个格子

大概就是一个kdtree吧。。。更新子矩阵就像线段树那样放标记就行了,删除的时候像替罪羊树那样懒惰删除。

代码

#include <bits/stdc++.h>
#define N 70005
#define M 150005
#define getchar nc
using namespace std;
int n,m,w,h;
int px[N],py[N];
int P[M],T[M],L[M],R[M],D[M],U[M];
set<pair<int,int> > s[N<<2];
vector<int> nv[N];
struct node{
    int dis,pos;
    bool operator < (const node &x) const{
        return x.dis<dis;
    }
};
priority_queue<node> q;
int dis[N+M],vis[N+M];
void modify(register int x,register int l,register int r,register int id)
{
    s[x].insert(make_pair(py[id],id));
    if(l==r)
        return;
    int mid=l+r>>1;
    if(px[id]<=mid)
        modify(x<<1,l,mid,id);
    else
        modify(x<<1|1,mid+1,r,id);
}
void change(register int x,register int l,register int r,register int id)
{
    if(L[id]<=l&&r<=R[id])
    {
        set<pair<int,int> >::iterator it;
        while(19260817)
        {
            it=s[x].lower_bound(make_pair(D[id],-1));
            if(it==s[x].end()||it->first>U[id])
                break;
            int to=it->second;
            if(dis[to]>dis[id+n])
            {
                dis[to]=dis[id+n];
                q.push((node){dis[to],to});
            }
            s[x].erase(it);
        }
        return;
    }
    int mid=l+r>>1;
    if(L[id]<=mid)
        change(x<<1,l,mid,id);
    if(R[id]>mid)
        change(x<<1|1,mid+1,r,id);
}
int main()
{
    n=read(),m=read(),w=read(),h=read();
    for(register int i=1;i<=n;++i)
    {
        px[i]=read(),py[i]=read();
        if(i!=1)
            modify(1,1,w,i);
    }
    for(register int i=1;i<=m;++i)
    {
        P[i]=read(),T[i]=read(),L[i]=read(),R[i]=read(),D[i]=read(),U[i]=read();
        nv[P[i]].push_back(i+n);
    }
    for(register int i=1;i<=n;++i)
        dis[i]=1926081700,vis[i]=0;
    dis[1]=0;
    q.push((node){0,1});
    while(!q.empty())
    {
        node tmp=q.top();
        q.pop();
        int x=tmp.pos;
        if(vis[x])
            continue;
        vis[x]=1;
        if(x<=n)
        {
            for(register int i=0;i<nv[x].size();++i)
            {
                int y=nv[x][i];
                dis[y]=dis[x]+T[y-n];
                q.push((node){dis[y],y});
            }
        }
        else
            change(1,1,w,x-n);
    }
    for(register int i=2;i<=n;++i)
        write(dis[i]),puts("");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值