P4899 [IOI2018] werewolf 狼人
题目描述
在日本的茨城县内共有\(N\)个城市和\(M\)条道路。这些城市是根据人口数量的升序排列的,依次编号为\(0\)到\(N-1\)。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。
你计划了\(Q\)个行程,这些行程分别编号为\(0\)至\(Q-1\)。第\(i(0 \leq i \leq Q - 1)\)个行程是从城市\(S_i\)到城市\(E_i\)。
你是一个狼人。你有两种形态:人形和狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在\(S_i\)或\(E_i\)内)变身。
狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程\(i(0 \leq i \leq Q - 1)\),都有两个阈值 \(L_i\)和\(R_i(0 \leq L_i \leq R_i \leq N - 1)\),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市\(0, 1, \ldots , L_i - 1\);而当你是狼形时,则必须避开城市\(R_i + 1, R_i + 2, \ldots , N - 1\)。这就是说,在行程\(i\)中,你必须在城市\(L_i, L_i + 1, \ldots , R_i\)中的其中一个城市内变身。
你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市\(S_i\)走到城市\(E_i\)。你的路线可以有任意长度。
题解
首先出于个人习惯问题将城市的编号统一加\(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;}