洛谷传送门
LOJ传送门
题目描述
在日本的茨城县内共有 N N N 个城市和 M M M 条道路。这些城市是根据人口数量的升序排列的,依次编号为 0 0 0 到 N − 1 N - 1 N−1。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。
你计划了 Q Q Q 个行程,这些行程分别编号为 0 0 0 至 Q − 1 Q - 1 Q−1。第 $i(0 \leq i \leq Q - 1) $个行程是从城市 S i S_i Si 到城市 E i E_i Ei。
你是一个狼人。你有两种形态:人形和狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在 S i S_i Si 或 E i E_i Ei内)变身。
狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程 i ( 0 ≤ i ≤ Q − 1 ) i(0 \leq i \leq Q - 1) i(0≤i≤Q−1),都有两个阈值 $L_i $和 R i ( 0 ≤ L i ≤ R i ≤ N − 1 ) R_i(0 \leq L_i \leq R_i \leq N - 1) Ri(0≤Li≤Ri≤N−1),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市 0 , 1 , . . . , L i − 1 0, 1, ... , L_i - 1 0,1,...,Li−1 ;而当你是狼形时,则必须避开城市 R i + 1 , R i + 2 , . . . , N − 1 R_i + 1, R_i + 2, ..., N - 1 Ri+1,Ri+2,...,N−1。这就是说,在行程 i i i 中,你必须在城市 L i , L i + 1 , . . . , R i L_i, L_i + 1, ..., R_i Li,Li+1,...,Ri 中的其中一个城市内变身。
你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市 S i S_i Si 走到城市 E i E_i Ei。你的路线可以有任意长度。
输入输出格式
输入格式:
输入的第一行包含三个正整数 N , M , Q N, M, Q N,M,Q,其意义见题目描述。
接下来 M M M 行,每行包含两个非负整数。在这 $M $行中,第 j j j 行的两个非负整数分别表示 X j − 1 , Y j − 1 X_{j - 1}, Y_{j - 1} Xj−1,Yj−1,即编号为 j − 1 j - 1 j−1 的道路连接的两个城市的编号。
接下来 Q Q Q 行,每行包含四个非负整数。在这 Q Q Q 行中,第 i i i 行的四个非负整数分别表示 S i − 1 , E i − 1 , L i − 1 , R i − 1 S_{i - 1}, E_{i - 1}, L_{i - 1}, R_{i - 1} Si−1,Ei−1,Li−1,Ri−1,即编号为 i − 1 i - 1 i−1 的行程的起点城市编号、终点城市编号以及两个阈值。
输出格式:
输出包含 Q Q Q 行,每行包含一个非 0 0 0 即 1 1 1 的整数。第 i i i 行的整数表示对于编号为 i − 1 i - 1 i−1的行程,是否能从城市 S i − 1 S_{i - 1} Si−1 走至城市 E i − 1 E_{i - 1} Ei−1,若能够,那么输出整数为 1 1 1;若不能,那么输出整数为 0 0 0。
输入输出样例
输入样例#1:
6 6 3
5 1
1 2
1 3
3 4
3 0
5 2
4 2 1 2
4 2 2 2
5 4 3 4
输出样例#1:
1
0
0
输入样例#2:
10 9 10
6 7
1 5
8 0
2 9
9 4
2 7
8 5
6 0
3 4
4 9 0 9
8 1 8 9
1 8 1 8
8 3 5 5
8 9 3 9
0 1 0 2
9 0 6 6
1 7 1 8
9 4 5 6
9 5 0 9
输出样例#2:
1
1
1
0
1
1
0
1
0
1
说明
限制条件
-
2 ≤ N ≤ 200 , 000 2 \leq N \leq 200, 000 2≤N≤200,000
-
N − 1 ≤ M ≤ 400 , 000 N - 1 \leq M \leq 400, 000 N−1≤M≤400,000
-
1 ≤ Q ≤ 200 , 000 1 \leq Q \leq 200, 000 1≤Q≤200,000
对于每个 0 ≤ j ≤ M − 1 0 \leq j \leq M - 1 0≤j≤M−1
- 0 ≤ X j ≤ N − 1 0 \leq X_j \leq N - 1 0≤Xj≤N−1
- 0 ≤ Y j ≤ N − 1 0 \leq Y_j \leq N - 1 0≤Yj≤N−1
- X j ≠ Y j X_j \neq Y_j Xj̸=Yj
-
你可以通过道路由任意一个城市去另外任意一个城市。
-
每一对城市最多只由一条道路直接连起来。换言之,对于所有 0 ≤ j < k ≤ M − 1 0 \leq j < k \leq M - 1 0≤j<k≤M−1,都有 $(X_j, Y_j) \neq (X_k, Y_k) $和 ( Y j , X j ) ≠ ( X k , Y k ) (Y_j, X_j) \neq (X_k, Y_k) (Yj,Xj)̸=(Xk,Yk)
-
对于每个 0 ≤ i ≤ Q − 1 0 \leq i \leq Q - 1 0≤i≤Q−1
- 0 ≤ L i ≤ S i ≤ N − 1 0 \leq L_i \leq S_i \leq N - 1 0≤Li≤Si≤N−1
- 0 ≤ E i ≤ R i ≤ N − 1 0 \leq E_i \leq R_i \leq N - 1 0≤Ei≤Ri≤N−1
- S i ≠ E i S_i \neq E_i Si̸=Ei
- L i ≤ R i L_i \leq R_i Li≤Ri
子任务
- 1.(7 分) N ≤ 100 N \leq 100 N≤100, M ≤ 200 M \leq 200 M≤200, Q ≤ 100 Q \leq 100 Q≤100
- 2.(8 分) N ≤ 3 , 000 N \leq 3, 000 N≤3,000, M ≤ 6 , 000 M \leq 6, 000 M≤6,000, Q ≤ 3 , 000 Q \leq 3, 000 Q≤3,000
- 3.(34 分) M = N − 1 M = N - 1 M=N−1 且每个城市最多与两条路相连 (所有城市是以一条直线的形式连起来)
- 4.(51 分) 没有附加限制
解题分析
可以发现, 我们要求的是两个满足要求的连通块是否有交。
这个要求又和点权有关, 那么我们把点权转到边权上去, 然后建立 k r u s k a l kruskal kruskal重构树, 再用 d f s dfs dfs序标记每个点, 就变成了经典的二维数点问题, 离线扫描线即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 400500
#define ll long long
#define lbt(i) ((i) & (-(i)))
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, m, q, qcnt, tot;
int ans[MX], tree[MX << 1];
struct Edge {int to, nex;};
struct E {int from, to, val;} e[MX];
IN bool cmp1(const E &x, const E &y) {return x.val > y.val;}
IN bool cmp2(const E &x, const E &y) {return x.val < y.val;}
IN void add(R int pos) {for (; pos <= tot; pos += lbt(pos)) tree[pos]++;}
IN int query(R int pos)
{
int ret = 0;
for (; pos; pos -= lbt(pos)) ret += tree[pos];
return ret;
}
struct Tree
{
bool typ; // 0 for man, 1 for wolf
int cnt, tot, dfn;
int lb[MX], rb[MX], head[MX], val[MX], fat[MX][20], bel[MX];
Edge edge[MX];
int find(R int now) {return now == bel[now] ? now : bel[now] = find(bel[now]);}
IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;}
void DFS(R int now)
{
lb[now] = ++dfn;
for (R int i = 1; i < 20; ++i)
{
fat[now][i] = fat[fat[now][i - 1]][i - 1];
if (!fat[now][i]) break;
}
for (R int i = head[now]; i; i = edge[i].nex)
{
fat[edge[i].to][0] = now;
DFS(edge[i].to);
}
rb[now] = dfn;
}
IN int up(R int now, R int bd)
{
int ret = now;
if (!typ)
{
for (R int i = 19; ~i; --i)
{
if (!fat[now][i]) continue;
if (val[fat[now][i]] < bd) continue;
ret = now = fat[now][i];
}
return ret;
}
else
{
for (R int i = 19; ~i; --i)
{
if (!fat[now][i]) continue;
if (val[fat[now][i]] > bd) continue;
ret = now = fat[now][i];
}
return ret;
}
}
}man, wolf;
struct OP
{
int typ, x, up, down, id;//0 for add, 1 for front, 2 for back
} que[MX << 1];
IN bool operator < (const OP &x, const OP &y)
{return x.x == y.x ? x.typ > y.typ : x.x < y.x;}
int main(void)
{
int s, t, foo, bar; man.typ = 0, wolf.typ = 1;
in(n), in(m), in(q); man.tot = wolf.tot = n;
for (R int i = 1; i <= m; ++i) in(e[i].from), in(e[i].to), e[i].from++, e[i].to++;//remember to add 1!
for (R int i = 1; i < MX; ++i) man.bel[i] = wolf.bel[i] = i;
for (R int i = 1; i <= m; ++i) e[i].val = min(e[i].from, e[i].to);
std::sort(e + 1, e + m + 1, cmp1);
for (R int i = 1; i <= m; ++i)
{
foo = man.find(e[i].from), bar = man.find(e[i].to);
if (foo ^ bar)
{
man.tot++;
man.add(man.tot, foo), man.add(man.tot, bar);
man.bel[foo] = man.bel[bar] = man.tot;
man.val[man.tot] = e[i].val;
}
}
for (R int i = 1; i <= m; ++i) e[i].val = max(e[i].from, e[i].to);
std::sort(e + 1, e + m + 1, cmp2);
for (R int i = 1; i <= m; ++i)
{
foo = wolf.find(e[i].from), bar = wolf.find(e[i].to);
if (foo ^ bar)
{
wolf.tot++;
wolf.add(wolf.tot, foo), wolf.add(wolf.tot, bar);
wolf.bel[foo] = wolf.bel[bar] = wolf.tot;
wolf.val[wolf.tot] = e[i].val;
}
}
man.DFS(man.tot), wolf.DFS(wolf.tot); tot = man.tot;
for (R int i = 1; i <= n; ++i) que[++qcnt] = {0, man.lb[i], wolf.lb[i], 0, 0};
for (R int i = 1; i <= q; ++i)
{
in(s), in(t), in(foo), in(bar);
++s, ++t, ++foo, ++bar;
if (s < foo || t > bar) continue;
foo = man.up(s, foo), bar = wolf.up(t, bar);
que[++qcnt] = {1, man.lb[foo], wolf.rb[bar], wolf.lb[bar], i};
que[++qcnt] = {2, man.rb[foo] + 1, wolf.rb[bar], wolf.lb[bar], i};
}
std::sort(que + 1, que + 1 + qcnt);
for (R int i = 1; i <= qcnt; ++i)
{
switch(que[i].typ)
{
case 0: add(que[i].up); break;
case 1: ans[que[i].id] = query(que[i].down - 1) - query(que[i].up); break;
case 2: ans[que[i].id] += query(que[i].up) - query(que[i].down - 1); break;
}
}
for (R int i = 1; i <= q; ++i) puts(ans[i] ? "1" : "0");
}