【IOI 2018】Werewolf 狼人
虽然作为IOI的Day1T3,但其实不是一道很难的题,或者说这道题其实比较套路吧。
接下来讲解一下这个题的做法:
如果你做过NOI 2018的Day1T1,并且看懂了题面,那你很快就会联想到这道题,因为两者都是问某一个点能到达的点集,只不过限制在点上还是边上的问题。
$Kruskal$重构树可以把在边上的限制转化成点上的,于是能解决NOI 2018的Day1T1;那么这道题就可以直接做,因为限制已经在点上了。
具体来讲,从$s$点只能走编号$>=l$的点,那么我们就构建一棵树,使得任意一个非根节点的标号都比它父亲都大。要做到这一点,只要从大到小枚举点$x$,尝试把它加入树中,枚举所有与他相邻且编号比它大的节点$y$,如果$x$和$y$不在一个连通块里,就让$y$的并查集的根成为$x$的儿子,并在并查集里也连起来。
我们要对$s$和$t$都做一遍,也就是从大到小建一棵树$Tu$,从小到大建一棵树$Tv$。那么从$s$出发只走$>=l$的点能走到的点在$Tu$上就是一个子树;对$t$同理。
我们能用树上倍增找到$s$能走到的$Tu$上的那个子树,和$t$能走到的$Tv$的那个子树,这两者在两棵树上分别都对应$Dfs$序上的一段区间。
我们需要找到一个能变身的地点,它要满足能从$s$和$t$都能走到它,也就是在这两段$Dfs$上的区间中存在相同的元素。如何判断两个序列上的两个区间是否同时拥有某个元素,一般情况下我不会做,但是由于每个元素都恰好在一个序列中出现了一次,我们可以利用这个特殊的性质来解决。一个询问的答案是$1$当且仅当存在一个点,其中它在第一个序列中的位置在所求区间内,第二个序列也是。我们发现这就是一个简单的二维数点,我们把所有点在第一个和第二个$Dfs$序中中出现的位置分别当成$(x,y)$,一个询问就是判定一个二维平面上的一个矩形内是否有点。用离线树状数组实现比较方便。
$\bigodot$技巧&套路:
- 点上限制和$Kruskal$重构树在边上的限制的联系
- 两个$Dfs$序区间是否有相同元素向数点问题的转化
由于这道题本质是一道传统题,所以我用了传统的实现方法:
#include <cstdio>
#include <vector>
#include <algorithm> using namespace std; const int LOG = ;
const int N = ; int n, m, nq, qi;
int ans[N];
vector<int> g[N]; struct Que {
int x, y, id, ty, f;
friend bool operator < (const Que &a, const Que &b) {
return (a.x != b.x)? (a.x < b.x) : (a.ty < b.ty);
}
} q[N * ]; struct Dsu {
int fa[N];
void Init(int n) {
for (int i = ; i <= n; ++i) fa[i] = i;
}
int Sk(int x) {
return fa[x] == x? x : fa[x] = Sk(fa[x]);
}
} U, V; struct Tree {
vector<int> g[N];
int tot, din[N], dfn[N], row[N], ed[N], dep[N], gr[LOG][N];
void Add(int x, int y) {
g[x].push_back(y), ++din[y];
}
void Dfs(int x) {
dfn[x] = ++tot, row[tot] = x;
for (int i = ; i < LOG; ++i)
if (gr[i - ][x]) gr[i][x] = gr[i - ][gr[i - ][x]];
for (int v : g[x]) {
gr[][v] = x, dep[v] = dep[x] + ;
Dfs(v);
}
ed[x] = tot;
}
void Build() {
int rt = -;
for (int i = ; i <= n; ++i) if (din[i] == ) rt = i;
if (rt == -) throw;
Dfs(rt);
}
} Tu, Tv; int Fly_u(int x, int lim) {
for (int i = LOG - ; ~i; --i)
if (Tu.gr[i][x] && Tu.gr[i][x] >= lim) x = Tu.gr[i][x];
return x;
}
int Fly_v(int x, int lim) {
for (int i = LOG - ; ~i; --i)
if (Tv.gr[i][x] && Tv.gr[i][x] <= lim) x = Tv.gr[i][x];
return x;
} namespace BIT {
int t[N];
void Add(int x) {
for (; x <= n; x += x & -x) ++t[x];
}
int Qr(int x, int r = ) {
for (; x; x -= x & -x) r += t[x];
return r;
}
} int main() {
scanf("%d%d%d", &n, &m, &nq);
for (int i = , x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
g[++x].push_back(++y), g[y].push_back(x);
}
U.Init(n), V.Init(n);
for (int i = n; i >= ; --i) {
for (int j : g[i]) {
if (j < i) continue;
int y = U.Sk(j);
if (i != y) U.fa[y] = i, Tu.Add(i, y);
}
}
for (int i = ; i <= n; ++i) {
for (int j : g[i]) {
if (j > i) continue;
int y = V.Sk(j);
if (i != y) V.fa[y] = i, Tv.Add(i, y);
}
}
Tu.Build(), Tv.Build();
for (int i = ; i <= n; ++i) {
q[i].x = Tu.dfn[i], q[i].y = Tv.dfn[i];
q[i].ty = ;
}
qi = n;
for (int i = , x, y, l, r; i <= nq; ++i) {
scanf("%d%d%d%d", &x, &y, &l, &r);
++x, ++y, ++l, ++r;
int u = Fly_u(x, l), v = Fly_v(y, r);
q[++qi] = (Que){ Tu.ed[u], Tv.ed[v], i, , };
if (Tu.dfn[u] > ) q[++qi] = (Que){ Tu.dfn[u] - , Tv.ed[v], i, , - };
if (Tv.dfn[v] > ) q[++qi] = (Que){ Tu.ed[u], Tv.dfn[v] - , i, , - };
if (Tu.dfn[u] > && Tv.dfn[v] > ) q[++qi] = (Que){ Tu.dfn[u] - , Tv.dfn[v] - , i, , };
}
sort(q + , q + + qi);
for (int i = ; i <= qi; ++i) {
if (q[i].ty == ) {
BIT::Add(q[i].y);
} else {
ans[q[i].id] += q[i].f * BIT::Qr(q[i].y);
}
}
for (int i = ; i <= nq; ++i) {
if (ans[i] < ) throw;
printf("%d\n", (bool)ans[i]);
} return ;
}
【IOI 2018】Werewolf 狼人的更多相关文章
- [IOI 2018] Werewolf
[题目链接] https://www.luogu.org/problemnew/show/P4899 [算法] 建出原图的最小/最大生成树的kruskal重构树然后二维数点 时间复杂度 ...
- [IOI2018] werewolf 狼人
[IOI2018] werewolf 狼人 IOI2018题解 (其实原题强制在线,要用主席树) 代码: 注意: 1.下标从0~n-1 2.kruskal重构树开始有n个节点,tot从n开始,++to ...
- [IOI2018] werewolf 狼人 kruskal重构树,主席树
[IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据 ...
- [LOJ2865] P4899 [IOI2018] werewolf 狼人
P4899 [IOI2018] werewolf 狼人 LOJ#2865.「IOI2018」狼人,第一次AC交互题 kruskal 重构树+主席树 其实知道重构树的算法的话,难度就主要在主席树上 习惯 ...
- 【IOI 2018】Combo 组合动作(模拟,小技巧)
题目链接 IOI的签到题感觉比NOI的签到题要简单啊,至少NOI同步赛我没有签到成功…… 其实这个题还是挺妙妙的,如果能够从题目出发,利用好限制,应该是可以想到的做法的. 接下来开始讲解具体的做法: ...
- 【IOI 2018】Highway 高速公路收费
这是一道极好的图论题,虽然我一开始只会做$18$分,后来会做$51$分,看着题解想了好久才会做(吐槽官方题解:永远只有一句话),但这的确是一道好题,值得思考,也能启发思维. 如果要讲这道题,还是要从部 ...
- 【IOI 2018】Doll 机械娃娃
我感觉这个题作为Day2T1,有一定的挑战性.为$Rxd$没有完成这道题可惜. 我觉得这道题,如果按照前几个部分分的思路来想,就有可能绕进错误的思路中.因为比如说每个传感器最多只在序列中出现$2$次, ...
- [Luogu4899][IOI2018] werewolf 狼人
luogu sol \(\mbox{IOI2018}\)的出题人有没有看过\(\mbox{NOI2018}\)的题目呀... \(\mbox{Kruskal}\)重构树+二维数点. 题目相当于是问你从 ...
- P4899 【[IOI2018] werewolf 狼人】
感觉已经几次碰到这种类型的题目了,写篇\(Blog\)总结一下 题意: 是否存在一条\((s_i, t_i)\)的路径,满足先只走编号不超过\(L_i\)的点,再走编号不超过\(R_i\)的点 \(S ...
随机推荐
- Linux命令的那些事(一)
回顾一下前文,三大主流操作系统 windows做的最好(更准确最早做图形化界面是windows)其实是图形化界面占有90%的市场份额(PC(个人电脑)机的市场)但是现在发展图形界面做的较好其实Unix ...
- C++ 学习笔记 变量和基本类型(一)
C++ 学习笔记 一.变量和基本类型概述 类型是所有程序的基础.类型告诉我们数据代表什么意思以及可以对数据执行哪些操作. c++基本类型: 字符型 整型 浮点型 c++ 还提供了可用于自定义数据类型的 ...
- Memcached服务器上实现多个实例(约约问题排查)
约约测试服上出行一个问题,司机收车失败. (1)经查看代码是null指针异常. 针对,之前,同套代码发布到华威测试服,未出现该问题,遂认定不是代码问题. (2)打印异常信息,获取null值异常的收车司 ...
- 【Unity Shader】渲染管线
流程概述 应用程序阶段 应用程序阶段,使用高级编程语言(C.C++.JAVA 等)进行开发,主要和CPU.内存打交道,诸如碰撞检测.场景图建立.空间八叉树更新.视锥裁剪等经典算法都在此阶段执行.在该阶 ...
- Web服务架构
# Web服务架构 ### Web服务模型-- 服务提供者.服务请求者.服务注册中心,服务注册中心是一个可选的角色. 现在的Web服务不仅限于WSDL,还有RESTful. - 服务提供者.即Web服 ...
- 【python 2.7】获取外部参数
import sys res_0 = sys.argv[0] res_1 = sys.argv[1] res_2 = sys.argv[2] print res_0 print res_1 print ...
- Xavier——Understanding the difficulty of training deep feedforward neural networks
1. 摘要 本文尝试解释为什么在深度的神经网络中随机初始化会让梯度下降表现很差,并且在此基础上来帮助设计更好的算法. 作者发现 sigmoid 函数不适合深度网络,在这种情况下,随机初始化参数会让较深 ...
- webpack构建Vue项目引入jQ时发生“'$' is defined but never used”的处理
今天公司需要新建个数据后台,就按照查到的方法构建了Vue框架的项目,引入jQ.bootstrap时,按照在线方法配置,发现 main.js 里的引用jQ一直显示红标,没多想,在按照网上配置完后,npm ...
- js中 null, undefined, 0,空字符串,false,不全等比较
null == undefined // true null == '' // false null == 0 // false null == false // false undefined = ...
- 数据库——SQL数据定义
数据定义 SQL的数据定义语句 操 作 对 象 操 作 方 式 创 建 删 除 修 改 表 CREATE TABLE DROP TABLE ALTER TABLE 视 图 CREATE ...