P4899 [IOI2018] werewolf 狼人
题目描述
在日本的茨城县内共有N个城市和M条道路。这些城市是根据人口数量的升序排列的,依次编号为0到N−1。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。
你计划了Q个行程,这些行程分别编号为0至Q−1。第i(0≤i≤Q−1)个行程是从城市Si到城市Ei。
你是一个狼人。你有两种形态:人形和狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在Si或Ei内)变身。
狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程i(0≤i≤Q−1),都有两个阈值 Li和Ri(0≤Li≤Ri≤N−1),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市0,1,…,Li−1;而当你是狼形时,则必须避开城市Ri+1,Ri+2,…,N−1。这就是说,在行程i中,你必须在城市Li,Li+1,…,Ri中的其中一个城市内变身。
你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市Si走到城市Ei。你的路线可以有任意长度。
题解
首先出于个人习惯问题将城市的编号统一加1变成[1,N]。
对于人形相当于从S出发只能走[L,N],而对于狼形相当于从T出发只能走[1,R]。
我们Kruskal建两棵树A和B。其中A满足子节点的编号大于父节点,B满足子节点的编号小于父节点,那么我们可以在两棵树上分别从S和T倍增,找到深度最小的满足两个区间限制的点,那么他的子树就都是合法的。
考虑一整棵子树的dfs序是一段区间,所以我们将点的编号的匹配转化成两段dfs序的匹配,看是否有点的dfs序分别在两棵树上满足两个区间。
这个过程我们可以使用离线树状数组类似二维数点实现,也可以用主席树以A树上的dfs序为外层下标,B树上的dfs序为内层下标实现。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5;
int n, m, Q, tot, head1[N], num, tr[N << 2], ans[N << 2];
struct node{int to, nex;}b[N << 2];
struct data{int x, y, val, id;}d[N << 2];
inline int read()
{
int x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * f;
}
struct tree1
{
int tot, head[N], fa[N], cnt, f[N][19], num, dfn[N], siz[N];
struct data{int to, nex;}a[N];
void add(int x, int y) {a[++ tot].to = y; a[tot].nex = head[x]; head[x] = tot;}
int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
void build()
{
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = n, x, y; i >= 1; i --) // n -- > 1
{
x = i;
for(int j = head1[i]; j; j = b[j].nex)
{
y = b[j].to;
if(x < y)
{
int xx = find(x), yy = find(y);
if(xx == yy) continue;
fa[yy] = xx; add(xx, yy);
if((++ cnt) == n - 1) return;
}
}
}
}
void dfs(int x, int fat)
{
f[x][0] = fat; siz[x] = 1; dfn[x] = ++ num;
for(int i = 1; i <= 18; i ++) f[x][i] = f[f[x][i - 1]][i - 1];
for(int i = head[x]; i; i = a[i].nex)
{
int y = a[i].to;
if(y == fat) continue;
dfs(y, x); siz[x] += siz[y];
}
}
int query(int x, int em)
{
for(int i = 18; i >= 0; i --) if(f[x][i] && f[x][i] >= em) x = f[x][i];
return x;
}
}A;
struct tree2
{
int tot, head[N], fa[N], cnt, f[N][19], num, dfn[N], siz[N];
struct data{int to, nex;}a[N];
void add(int x, int y) {a[++ tot].to = y; a[tot].nex = head[x]; head[x] = tot;}
int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
void build()
{
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1, x, y; i <= n; i ++) // 1 -- > n
{
x = i;
for(int j = head1[i]; j; j = b[j].nex)
{
y = b[j].to;
if(x > y)
{
int xx = find(x), yy = find(y);
if(xx == yy) continue;
fa[yy] = xx; add(xx, yy);
if((++ cnt) == n - 1) return;
}
}
}
}
void dfs(int x, int fat)
{
f[x][0] = fat; siz[x] = 1; dfn[x] = ++ num;
for(int i = 1; i <= 18; i ++) f[x][i] = f[f[x][i - 1]][i - 1];
for(int i = head[x]; i; i = a[i].nex)
{
int y = a[i].to;
if(y == fat) continue;
dfs(y, x); siz[x] += siz[y];
}
}
int query(int x, int em)
{
for(int i = 18; i >= 0; i --) if(f[x][i] && f[x][i] <= em) x = f[x][i];
return x;
}
}B;
void add(int x, int y) {b[++ tot].to = y; b[tot].nex = head1[x]; head1[x] = tot;}
bool cmp(const data & a, const data & b) {return a.x == b.x ? a.id < b.id : a.x < b.x;}
int lowbit(int x) {return x & (- x);}
void add(int x) {while(x <= n) {tr[x] ++; x += lowbit(x);}}
int query(int x) {int res = 0; while(x) {res += tr[x]; x -= lowbit(x);} return res;}
void work()
{
n = read(); m = read(); Q = read();
for(int i = 1, x, y; i <= m; i ++)
{
x = read() + 1; y = read() + 1;
add(x, y); add(y, x);
}
A.build(); B.build(); A.dfs(1, 0); B.dfs(n, 0);
for(int i = 1; i <= n; i ++) d[++ num] = data{A.dfn[i], B.dfn[i], 0, 0};
for(int i = 1, s, t, l, r; i <= Q; i ++)
{
s = read() + 1; t = read() + 1; l = read() + 1; r = read() + 1;
s = A.query(s, l); t = B.query(t, r);
int x = A.dfn[s] - 1, y = A.dfn[s] + A.siz[s] - 1;
int xx = B.dfn[t] - 1, yy = B.dfn[t] + B.siz[t] - 1;
d[++ num] = data{x, xx, 1, i}; d[++ num] = data{x, yy, -1, i};
d[++ num] = data{y, xx, -1, i}; d[++ num] = data{y, yy, 1, i};//
}
sort(d + 1, d + num + 1, cmp);
for(int i = 1; i <= num; i ++) if(d[i].val == 0) add(d[i].y); else ans[d[i].id] += d[i].val * query(d[i].y);
for(int i = 1; i <= Q; i ++) printf("%d\n", ans[i] == 0 ? 0 : 1);
}
int main() {return work(), 0;}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥