【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 ...
随机推荐
- 使用maven&&make-distribution.sh编译打包spark源码
1>基础环境准备: jdk1.8.0_101 maven 3.3.9scala2.11.8 安装好上述软件,配置好环境变量,并检查是否生效. 2>配置maven:intellij idea ...
- WebSocket抓包分析
转载自:https://www.cnblogs.com/songwenjie/p/8575579.html Chrome控制台 (1)F12进入控制台,点击Network,选中ws栏,注意选中Filt ...
- ClassLoader.loadClass()与Class.forName()的区别
ClassLoader.loadClass()与Class.forName()都是反射用来构造类的方法,但是他们的用法还是有一定区别的. 在讲区别之前,我觉得很有不要把类的加载过程在此整理一下. 在J ...
- HPUX系统启动后主机名为unknown的解决办法
HPUX系统启动完成后,主机名为unknown,查看/etc/rc.log出现如下报错: unknown:[/]grep -i error /etc/rc.log /sbin/rc1.d/S320 ...
- bitcoin PoW原理及区块创建过程
bitcoin PoW原理及区块创建过程 PoW 为了在点对点的基础上实现一个分布式时间戳服务器,我们需要使用PoW(Proof of Work)系统来达成共识.PoW过程就是寻找一个目标值的过程,当 ...
- RBC:Echo设备2020年可为亚马逊贡献100亿美元收入
BI 中文站 12 月 22 日报道 加拿大皇家银行资本市场(RBC Capital Markets)分析师马克-马哈尼(Mark Mahaney)表示,亚马逊是首批将智能音箱引进主流受众的公司之一, ...
- 允许使用root远程ssh登录(Ubuntu 16.04)
今天装了ubuntu16和17,发现还是ubuntu16看着顺眼,所以以后决定用ubuntu16, 然后想换语言发现更新失败,所以想换成中国的源,但是vm里面复制粘贴不了,所以想用secureCRT连 ...
- selenium+python 自动化
<a class="big_images_new" target="_blank" href="http://photo.xcar.com.cn ...
- iOS开发学习-资源打包
图片是被放到Images.xcassets中 1.部署版本在>=iOS8的时候,打包的资源包中的图片会被放到Assets.car 图片被压缩: 2.部署版本在<iOS8的时候,打包的资源包 ...
- 项目Beta冲刺(团队)第四天
1.昨天的困难 返回提问者昵称的时候返回信息不全,个别信息没有返回过去 一开始ProgressBar控件的显示有问题 需要实现类似聊天的功能,采用listview承载聊天内容,对于自定义适配器的构建使 ...