题目传送门:LOJ #546

题意简述:

题目说的很清楚了。

题解:

不包含起点或障碍物的连续的行或列缩成一行或一列,不会影响答案。

处理过后,新的网格图的行数和列数最多为 \(2k + 3\)。

考虑将同一行连续的不包含障碍物的格子标记为一个点,同一列同理。

这样处理过后,网格图对应的点数最多为 \(6k + 6\)。

某一行的无障碍连续段如果和某一列的无障碍连续段相交,就在它们所表示的点之间连一条权值为 \(1\) 的双向边。

从起点所在的行连续段和列连续段表示的 \(2\) 个源点开始求最短路,则给出终点的答案即为终点所在的行列连续段的距离的最小值。

因为边权为 \(1\),所以直接 BFS 就行了。

但是这样边数是 \(\mathcal O (k^2)\) 的,考虑使用主席树优化建边即可。

边权为 \(0\) 或者 \(1\),使用 01BFS 即可。注意不需要显式建边。

下面是代码,时间复杂度为 \(\mathcal O (k \log k)\):

#include <cstdio>
#include <algorithm>
#include <vector> const int Inf = 0x3f3f3f3f;
const int MK = 50005, MS = 2200005; int N, M, K, Q, Sx, Sy, cnt;
struct dot { int x, y; } obs[MK];
inline bool cmp(dot p, dot q) { return p.x == q.x ? p.y < q.y : p.x < q.x; }
int xdx[MK * 2], xdy[MK * 2], xcx, xcy;
std::vector<int> vecx[MK * 2], idx[MK * 2], vecy[MK * 2], idy[MK * 2];
int typ[MK * 6], rc[MK * 6], lb[MK * 6], rb[MK * 6];
int pjx[MK * 2], pjy[MK * 2];
inline int gIdX(int x, int y) { return idx[x][std::lower_bound(vecx[x].begin(), vecx[x].end(), y) - vecx[x].begin()]; }
inline int gIdY(int x, int y) { return idy[y][std::lower_bound(vecy[y].begin(), vecy[y].end(), x) - vecy[y].begin()]; } void Init() {
scanf("%d%d%d%d", &N, &M, &K, &Q);
for (int i = 1, x, y; i <= K + 1; ++i) {
scanf("%d%d", &x, &y);
if (i <= K) obs[i].x = x, obs[i].y = y;
else obs[0].x = x, obs[0].y = y;
xdx[++xcx] = x, xdy[++xcy] = y;
if (x > 1) xdx[++xcx] = x - 1;
if (y > 1) xdy[++xcy] = y - 1;
} xdx[++xcx] = N, xdy[++xcy] = M;
std::sort(xdx + 1, xdx + xcx + 1), N = xcx = std::unique(xdx + 1, xdx + xcx + 1) - xdx - 1;
std::sort(xdy + 1, xdy + xcy + 1), M = xcy = std::unique(xdy + 1, xdy + xcy + 1) - xdy - 1;
for (int i = 0; i <= K; ++i) {
obs[i].x = std::lower_bound(xdx + 1, xdx + xcx + 1, obs[i].x) - xdx;
obs[i].y = std::lower_bound(xdy + 1, xdy + xcy + 1, obs[i].y) - xdy;
} Sx = obs[0].x, Sy = obs[0].y;
std::sort(obs + 1, obs + K + 1, cmp);
for (int i = 1, x, y, p; i <= K; ++i) {
x = obs[i].x, y = obs[i].y;
p = vecx[x].empty() ? 0 : vecx[x].back();
if (y - p >= 2) {
idx[x].push_back(++cnt);
typ[cnt] = 1, rc[cnt] = x;
lb[cnt] = p + 1, rb[cnt] = y - 1;
} else idx[x].push_back(0);
p = vecy[y].empty() ? 0 : vecy[y].back();
if (x - p >= 2) {
idy[y].push_back(++cnt);
typ[cnt] = 2, rc[cnt] = y;
lb[cnt] = p + 1, rb[cnt] = x - 1;
} else idy[y].push_back(0);
vecx[x].push_back(y);
vecy[y].push_back(x);
}
for (int i = 1, p; i <= N; ++i) {
p = vecx[i].empty() ? 0 : vecx[i].back();
if (p < M) {
idx[i].push_back(++cnt);
typ[cnt] = 1, rc[cnt] = i;
lb[cnt] = p + 1, rb[cnt] = M;
} else idx[i].push_back(0);
vecx[i].push_back(M + 1);
}
for (int i = 1, p; i <= M; ++i) {
p = vecy[i].empty() ? 0 : vecy[i].back();
if (p < N) {
idy[i].push_back(++cnt);
typ[cnt] = 2, rc[cnt] = i;
lb[cnt] = p + 1, rb[cnt] = N;
} else idy[i].push_back(0);
vecy[i].push_back(N + 1);
}
} #define mid ((l + r) >> 1)
int rtx[MK * 2], rty[MK * 2];
int rdx[MK], rdy[MK], rcx, rcy, nds;
int ls[MS], rs[MS];
std::vector<int> *id;
void Build(int &rt, int l, int r) {
if (l == r) { rt = id[l][0]; return ; }
rt = ++nds;
Build(ls[rt], l, mid), Build(rs[rt], mid + 1, r);
}
void Mdf(int &rt, int l, int r, int p, int x) {
if (l == r) { rt = x; return ; }
++nds, ls[nds] = ls[rt], rs[nds] = rs[rt], rt = nds;
if (p <= mid) Mdf(ls[rt], l, mid, p, x);
else Mdf(rs[rt], mid + 1, r, p, x);
} void Link() {
nds = cnt;
id = idy, Build(rdx[0], 1, M);
for (int i = 1; i <= N; ++i) {
for (auto v : vecx[i]) if (v <= M) {
int id = idy[v][++pjx[v]];
if (id) Mdf(rdx[rcx + 1] = rdx[rcx], 1, M, v, id), ++rcx;
} rtx[i] = rdx[rcx];
}
id = idx, Build(rdy[0], 1, N);
for (int i = 1; i <= M; ++i) {
for (auto v : vecy[i]) if (v <= N) {
int id = idx[v][++pjy[v]];
if (id) Mdf(rdy[rcy + 1] = rdy[rcy], 1, N, v, id), ++rcy;
} rty[i] = rdy[rcy];
}
} int vis[MS], dis[MS], que[MS * 2], ql, qr;
void Ins(int rt, int l, int r, int a, int b, int x) {
if (!rt || r < a || b < l) return ;
if (a <= l && r <= b) {
if (dis[rt] > x + 1) dis[rt] = x + 1, que[++qr] = rt;
return ;
}
Ins(ls[rt], l, mid, a, b, x);
Ins(rs[rt], mid + 1, r, a, b, x);
}
void BFS() {
for (int i = 1; i <= nds; ++i) dis[i] = Inf;
int qwqx = gIdX(Sx, Sy), qwqy = gIdY(Sx, Sy);
ql = MS + 1, qr = MS;
dis[qwqx] = dis[qwqy] = 0;
que[++qr] = qwqx, que[++qr] = qwqy;
while (ql <= qr) {
int u = que[ql++], d = dis[u];
if (vis[u]) continue;
vis[u] = 1;
if (u <= cnt) {
Ins((typ[u] == 1 ? rtx : rty)[rc[u]], 1, (typ[u] == 1 ? M : N), lb[u], rb[u], d);
} else {
if (ls[u] && dis[ls[u]] > d) dis[ls[u]] = d, que[--ql] = ls[u];
if (rs[u] && dis[rs[u]] > d) dis[rs[u]] = d, que[--ql] = rs[u];
}
}
} int main() {
Init();
Link();
BFS();
while (Q--) {
int x, y;
scanf("%d%d", &x, &y);
x = std::lower_bound(xdx + 1, xdx + xcx + 1, x) - xdx;
y = std::lower_bound(xdy + 1, xdy + xcy + 1, y) - xdy;
int vx = gIdX(x, y), vy = gIdY(x, y);
printf("%d\n", dis[vx] == Inf ? -1 : std::min(dis[vx], dis[vy]));
}
return 0;
}

LOJ 546: 「LibreOJ β Round #7」网格图的更多相关文章

  1. [LOJ#531]「LibreOJ β Round #5」游戏

    [LOJ#531]「LibreOJ β Round #5」游戏 试题描述 LCR 三分钟就解决了问题,她自信地输入了结果-- > -- 正在检查程序 -- > -- 检查通过,正在评估智商 ...

  2. [LOJ#530]「LibreOJ β Round #5」最小倍数

    [LOJ#530]「LibreOJ β Round #5」最小倍数 试题描述 第二天,LCR 终于启动了备份存储器,准备上传数据时,却没有找到熟悉的文件资源,取而代之的是而屏幕上显示的一段话: 您的文 ...

  3. [LOJ#516]「LibreOJ β Round #2」DP 一般看规律

    [LOJ#516]「LibreOJ β Round #2」DP 一般看规律 试题描述 给定一个长度为 \(n\) 的序列 \(a\),一共有 \(m\) 个操作. 每次操作的内容为:给定 \(x,y\ ...

  4. [LOJ#515]「LibreOJ β Round #2」贪心只能过样例

    [LOJ#515]「LibreOJ β Round #2」贪心只能过样例 试题描述 一共有 \(n\) 个数,第 \(i\) 个数 \(x_i\) 可以取 \([a_i , b_i]\) 中任意值. ...

  5. [LOJ#525]「LibreOJ β Round #4」多项式

    [LOJ#525]「LibreOJ β Round #4」多项式 试题描述 给定一个正整数 k,你需要寻找一个系数均为 0 到 k−1 之间的非零多项式 f(x),满足对于任意整数 x 均有 f(x) ...

  6. [LOJ#526]「LibreOJ β Round #4」子集

    [LOJ#526]「LibreOJ β Round #4」子集 试题描述 qmqmqm有一个长为 n 的数列 a1,a2,……,an,你需要选择集合{1,2,……,n}的一个子集,使得这个子集中任意两 ...

  7. [LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机)

    [LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机) 试题描述 IOI 的比赛开始了.Jsp 和 Rlc 坐在一个角落,这时他们听到了一个异样的声音 …… 接着他们发现自己收 ...

  8. loj #547. 「LibreOJ β Round #7」匹配字符串

    #547. 「LibreOJ β Round #7」匹配字符串   题目描述 对于一个 01 串(即由字符 0 和 1 组成的字符串)sss,我们称 sss 合法,当且仅当串 sss 的任意一个长度为 ...

  9. loj #535. 「LibreOJ Round #6」花火 树状数组求逆序对+主席树二维数点+整体二分

    $ \color{#0066ff}{ 题目描述 }$ 「Hanabi, hanabi--」 一听说祭典上没有烟火,Karen 一脸沮丧. 「有的哦-- 虽然比不上大型烟花就是了.」 还好 Shinob ...

随机推荐

  1. linux学习(六)文件基本属性

    Linux系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限.为了保护系统的安全性,Linux系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定. 在Linux中我们可 ...

  2. df 查看磁盘使用情况

    1.查看磁盘使用情况 2.查看boot目录详情 3.提取已使用的百分比 4.切割提取出数字

  3. 项目那几步走:先配置setting路径文件、创建数据库、执行数据库迁移命令、配置mysql数据库信息、注册app、注释中间件、pymysql替换mysqldb-配置urls路由-继续视图函数-然后HTML页面展示-HTML里面导入css文件、models配置数据库表、

    django使用mysql数据库: 首先cmd创建库 1.settings: """Django settings for day42 project. Generate ...

  4. TestNG.xml 配置

    <packages>表示以测试类所在的包的方式定义测试用例,包中的所有测试类都被涉及,粒度较大. <?xml version="1.0" encoding=&qu ...

  5. Vue indent eslint缩进webstorm冲突解决

    参考教程 官方回复 ESlint设置 rules: { 'no-multiple-empty-lines': [1, {max: 3}], // 控制允许的最多的空行数量 'vue/script-in ...

  6. Project Euler Problem 675

    ORZ foreverlasting聚聚,QQ上问了他好久才会了这题(所以我们又聊了好久的Gal) 我们先来尝试推导一下\(S\)的性质,我们利用狄利克雷卷积来推: \[2^\omega=I\ast| ...

  7. 第04组 Alpha冲刺(1/4)

    队名:斗地组 组长博客:地址 作业博客:Alpha冲刺(1/4) 各组员情况 林涛(组长) 过去两天完成了哪些任务: 1.安排好各个组员的任务 2.收集各个组员的进度 3.写页面 4.写博客 展示Gi ...

  8. 阿里Sentinel整合Zuul网关详解

    前面我们讲解了Sentinel整合Spring Cloud Gateway,详细请查看文章:阿里Sentinel支持Spring Cloud Gateway啦 目前来说,大部分公司线上的网关应该是Zu ...

  9. C++ 回调函数的多种用法

    什么是回调函数, 就是以函数指针做参数传递给另一个函数称之为回调函数, 字面意思很简单, 但就这几个字想理解回调函数, 那又很难.因此别就这这字面意思, 只要知道怎么用, 在什么情况下用就行了 什么场 ...

  10. js json字符串与json对象互相转换(最全)

      1.json字符串转json对象 使用场景:通常在取json字符串里具体的值时,会用到. var jsonString = '{"name":"Marydon&quo ...