【HNOI 2018】游戏
Problem
Description
一次小 \(G\) 和小 \(H\) 在玩寻宝游戏,有 \(n\) 个房间排成一列,编号为 \(1,2,…,n\),相邻房间之间都有 \(1\) 道门。其中一部分门上有锁(因此需要对应的钥匙才能开门),其余的门都能直接打开。
现在小 \(G\) 告诉了小 \(H\) 每把锁的钥匙在哪个房间里(每把锁有且只有一把钥匙),并作出 \(p\) 次指示:第 \(i\) 次让小 H 从第 \(S_i\) 个房间出发,去第 \(T_i\) 个房间寻宝。但是小 \(G\) 有时会故意在指令里放入死路,而小 \(H\) 也不想浪费多余的体力去尝试,于是想事先调查清楚每次的指令是否存在一条通路。
你是否能为小 \(H\) 作出解答呢?
Input Format
第一行三个整数\(n\),\(m\),\(p\),代表共有 \(n\) 个房间,\(m\) 道门上了锁,以及 \(p\) 个询问。
接下来 \(m\) 行每行有两个整数\(x\),\(y\),代表第 \(x\) 到第 \(x + 1\) 个房间的门上有把锁,并且这把锁的钥匙被放在了第 \(y\) 个房间里。输入保证 \(x\) 不重复。
接下来 \(p\) 行,其中第 \(i\) 行是两个整数 \(S_i\),\(T_i\),代表一次询问。
Output Format
输出 \(m\) 行,每行一个大写的 YES
或 NO
分别代表能或不能到达。
Sample
Input 1
5 4 5
1 3
2 2
3 1
4 4
2 5
3 5
4 5
2 1
3 1
Output 1
YES
NO
YES
YES
NO
Input 2
此组样例满足特性:\(y \le x\) 恒成立
7 5 4
2 2
3 3
4 2
5 3
6 6
2 1
3 4
3 7
4 5
Output 2
YES
YES
NO
NO
Explanation
Explanation for Input 1
第一个询问 \(S = 2\)、\(T = 5\) 的一条可行路线是:\(2 \rightarrow 3 \rightarrow 2 \rightarrow 1 \rightarrow 2 \rightarrow 3 \rightarrow 4 \rightarrow 5\)。
Explanation for Input 2
第一个询问 \(2\) 和 \(1\) 房间之间没有锁所以为一条通路。
Range
测试点编号 | n | m | 其他特性 |
---|---|---|---|
1 | \(\le 1000\) | \(\le 1000\) | 无 |
2 | \(\le 1000\) | \(\le 1000\) | 无 |
3 | \(\le 10^5\) | \(\le 10^5\) | \(y \le x\) 恒成立 |
4 | \(\le 10^5\) | \(\le 10^5\) | \(y \le x\) 恒成立 |
5 | \(\le 10^5\) | \(\le 10^5\) | 无 |
6 | \(\le 10^5\) | \(\le 10^5\) | 无 |
7 | \(\le 10^6\) | \(\le 10^6\) | \(y \le x\) 恒成立 |
8 | \(\le 10^6\) | \(\le 10^6\) | \(y \le x\) 恒成立 |
9 | \(\le 10^6\) | \(\le 10^6\) | 无 |
10 | \(\le 10^6\) | \(\le 10^6\) | 无 |
Algorithm
线段树,单调栈。
Mentality
基本思路:设 \(r[i]\) 与 \(l[i]\) 为从 \(i\) 出发的能到达的区间,求出后对于每个询问 \(O(1)\) 判断就好。
这一题我们先从 \(60\) 分的做法看起:
- 暴力拓展左右端点,复杂度 \(O(n^2)\),\(20pts\) 。
- 对于所有 \(y\le x\) 的点用单调栈,\(40pts\) 。
暴力并不难,我们看看 \(40pts\) 的单调栈怎么做。其实不能算得上是什么神仙思路,类似于并查集,各扇门将序列分成了一些小区间,如果你通过此区间能到达其他区间,那么其他区间能到达的所有区间你也能到达。这就是此题单调栈的思路。
首先,既然所有 \(y\le x\) ,可以知道,我们向左走,一旦碰到一扇门就肯定走不过去了,那么我们只需要考虑最多能向右走多远就行了。
这个向右走的过程我们使用单调栈来维护:如果我们此时能到达的区间里有通向右边相邻区间的钥匙,那一旦到达当前区间,我必定能到达下一个区间,那么将两个区间合并为一个区间即可。
此处应有小代码:
bool pd(int x)
{
return key[r[x]]>=l[x]&&key[r[x]]<=r[x];
}
for(int i=n;i>=1;i--)
{
stack[++top]=r[i];//r[i]为初始区间右端点
while(pd(i))//如果当前区间右端点的钥匙在区间内
r[i]=stack[--top];//更新右端点并栈内合并区间
}
\(40pts\) 解决了,那 \(100\) 分呢?
其实也很简单,我们唯一的难点仅在于左端点也会更新。所以我们只需要能在当前右端点条件下快速求出左端点能扩展到多远即可。
根据 \(y\le x\) 的情况,我们可以类推得到:向左走时,一旦钥匙在门左边,那就肯定过不去;向右走时,一旦钥匙在门右边,那肯定也过不去。
于是我们可以求出 \(ll[i]\) 与 \(rr[i]\) 来代表 \(i\) 所能到达区间的理论上界 (最大的左右端点只可能是 \(ll\) 与 \(rr\))。
那不难发现,我当前能走到的区间是 \([l,r]\) ,而对于 \(l\) 的扩展,一定能扩展到 \([ll,l-1]\) 里最右边的,钥匙位置 \(>r\) 的位置后边。
为什么呢?因为根据我们的筛法,区间 \([ll,l-1]\) 内的门的钥匙肯定都在门右边 (不然 \(ll\) 只会更右) ,则此区间内门的钥匙要么 \(\le r\) 要么 \(>r\) ,同时又在自己右边,那钥匙位置 \(\le r\) 的门肯定都可以打开的呀!
那么和之前是一模一样的,只是判断单调栈弹出之前先更新左端点即可:
bool pd(int x)
{
update_l(x);
return key[r[x]]>=l[x]&&key[r[x]]<=r[x];
}
for(int i=n;i>=1;i--)
{
stack[++top]=r[i];
while(pd(i))
r[i]=stack[--top];
}
至于具体怎么更新左端点呢?我们只需要找到区间 \([ll,l-1]\) 中,第一个 \(\ge r[i]\) 的位置就好。我们可以用线段树维护区间钥匙位置最大值,并在线段树上查找就好,设查找到的位置为 \(pos\) ,左端点更新为 \(pos+1\) 即可。
Code
#include <cstdio>
#include <iostream>
using namespace std;
#define ls (o << 1)
#define rs ((o << 1) + 1)
#define mid ((l + r) >> 1)
int n, m, Q, key[1000001], ll[1000001], rr[1000001], l[1000001], r[1000001];
int now, top, stack[1000001];
int L, R, X, ans, maxx[4000001];
void build(int o, int l, int r) {
if (l == r) {
maxx[o] = key[l];
return;
}
build(ls, l, mid), build(rs, mid + 1, r);
maxx[o] = max(maxx[ls], maxx[rs]);
}
void query(int o, int l, int r) {
if (ans) return;
if (l == r) {
ans = l;
return;
}
if (mid < R && maxx[rs] > X) query(rs, mid + 1, r); //显然先找右区间哇
if (mid >= L && maxx[ls] > X) query(ls, l, mid);
}
bool pd(int x) {
L = ll[x], R = l[x] - 1, X = r[x], ans = 0;
query(1, 1, n);
l[x] = !ans ? ll[x] : ans + 1; //更新左端点
return key[r[x]] >= l[x] && key[r[x]] <= r[x];
}
int main() {
freopen("4436.in", "r", stdin);
freopen("4436.out", "w", stdout);
cin >> n >> m >> Q;
int x, t, now, tp;
for (int i = 1; i <= m; i++) scanf("%d", &x), scanf("%d", &key[x]);
now = 1, tp = 1;
for (int i = 1; i <= n; i++) {
if (key[i - 1]) {
now = i;
if (key[i - 1] < i) tp = i;
}
ll[i] = tp, l[i] = now;
}
now = n, tp = n;
for (int i = n; i >= 1; i--) {
if (key[i]) {
now = i;
if (key[i] > i) tp = i;
}
rr[i] = tp, r[i] = now;
} //处理出 l,r,ll,rr
build(1, 1, n); //建树
stack[++top] = n;
for (int i = n; i >= 1; i--) {
stack[++top] = r[i];
while (pd(i)) r[i] = stack[--top];
} //求出 l,r
while (Q--) {
scanf("%d%d", &x, &t);
printf(t >= l[x] && t <= r[x] ? "YES\n" : "NO\n");
}
}
【HNOI 2018】游戏的更多相关文章
- [HNOI 2018]游戏
Description 题库链接 有 \(n\) 个房间排成一列,编号为 \(1,2,...,n\) ,相邻的房间之间都有一道门.其中 \(m\) 个门上锁,其余的门都能直接打开.现在已知每把锁的钥匙 ...
- 【HNOI 2018】寻宝游戏
Problem Description 某大学每年都会有一次 \(Mystery\ Hunt\) 的活动,玩家需要根据设置的线索解谜,找到宝藏的位置,前一年获胜的队伍可以获得这一年出题的机会. 作为新 ...
- [HNOI 2018]寻宝游戏
Description 题库链接 给出 \(n\) 个 \(m\) 位的二进制数,在每一个二进制数间插入一个 & 或 | ,第 \(0\) 个数为 \(0\) , \(0,1\) 间也要插入符 ...
- 【题解】Luogu P4436 [HNOI/AHOI2018]游戏
原题传送门 \(n^2\)过百万在HNOI/AHOI2018中真的成功了qwqwq 先将没门分格的地方连起来,枚举每一个块,看向左向右最多能走多远,最坏复杂度\(O(n^2)\),但出题人竟然没卡(建 ...
- HNOI 2018 简要题解
寻宝游戏 毒瘤题. 估计考试只会前30pts30pts30pts暴力然后果断走人. 正解是考虑到一个数&1\&1&1和∣0|0∣0都没有变化,&0\&0& ...
- [洛谷P4436] HNOI/AHOI2018 游戏
问题描述 一次小G和小H在玩寻宝游戏,有n个房间排成一列,编号为1,2,...,n,相邻的房间之间都有一道门.其中一部分门上锁(因此需要有对应的钥匙才能开门),其余的门都能直接打开.现在小G告诉了小H ...
- [HNOI 2018]道路
Description 题库链接 给出一棵含有 \(n\) 个叶子节点的二叉树,对于每个非叶子节点的节点,其与左儿子相连的边为公路,其与右儿子相连的边为铁路.对于每个节点,选择一条与其儿子相连的铁路或 ...
- [HNOI 2018]排列
Description 题库链接 给定 \(n\) 个整数 \(a_1, a_2, \dots, a_n, 0 \le ai \le n\) ,以及 \(n\) 个整数 \(w_1, w_2, \do ...
- [HNOI/AHOI2018]游戏
题目描述 https://lydsy.com/JudgeOnline/upload/201804/%E6%B9%96%E5%8D%97%E4%BA%8C%E8%AF%95%E8%AF%95%E9%A2 ...
随机推荐
- jquery截取地址栏中url参数的值
<script> /*http://127.0.0.9/index.php?s=/Home/Index/fangguan_shuju&zc=2*/ function getQuer ...
- jar命令打jar包
jar -cvfM0 cloudwarehouse-enter.jar ./BOOT-INF ./META-INF ./org jar -cvfM0 xxl-job-admin.war ./BOOT- ...
- td使用word-break: break-all;强制换行无效的解决
今天发现表格内容不换行,加了word-break: break-all;也没有效果,后来检查发现用户编辑的html内容包含了CSS,其中有一项: td {padding-top:1px;padding ...
- Prometheus监控学习记录
官方文档 Prometheus基础文档 从零开始:Prometheus 进阶之路:Prometheus —— 技巧篇 进阶之路:Prometheus —— 理解篇 prometheus的数据类型介绍 ...
- InnoDB中锁的算法(1)
Ⅰ.InnoDB锁算法的介绍 首先明确一点,锁锁住的是什么?锁锁住的是索引 Record Lock 单个行记录上的锁 Gap Lock 锁定一个范围,但不包含记录本身 Next-key Lock Ga ...
- Web开发——jQuery基础
参考: 参考W3School:jQuery 教程 参考:jQuery 参考手册 参考(常用):jQuery API 测试 JavaScript 框架库 - jQuery 测试 JavaScript 框 ...
- [摘录] 图灵机与lambda演算的关系
在阅读函数式编程相关资料时,看到如下一段话.感觉说的很好,可以帮助我这种学渣一点点的建立起整个知识体系. 以下片段,摘抄自豆瓣网友 赛义甫 的豆列 “逻辑与计算” 中的一段介绍. 莱布尼兹曾经有两个梦 ...
- [dpdk][hotplug] DPDK网卡设备热插拔
dpdk pci网卡设备的热插拔. 样例程序如下: ... ... static int driverctl(char* pci) { int pid; pid = fork(); ) { execl ...
- python 中的__new__与__init__
在Python中的class中有两个方法__new__与__init__,有什么区别呢? class TestCls(): """docstring for TestCl ...
- php-memcache基本用法
//create a memcache object $mem = new Memcache(); //create connection $mem->connect ...