[IOI2018] werewolf 狼人

[IOI2018] werewolf 狼人

可以考虑到每一条边能否通过对于人形态来说,取决于两点的最小值,对于狼形态来说取决两点的最大值。

那么我们不妨考虑将两条边的边权定为上述两者,这样就有两张联通的无向图,我们考虑既然不考虑路程的限制我们一个肯定是需要考虑最大生成树另外一个就是最小生成树。

使用 $\tt Kruscal$ 重构树建立两棵树,一个是大根堆另一个是小根堆。

对于两个 $x, y$ 能否到达取决于是否存在一个点同时是两者的儿子。

我们考虑维护 $\tt dfs$ 序,通过可持久化线段树来判断。

但是题目中还有 $L, R$ 的限制,我们考虑到对于一条路径我们其实对于树上一个点还能向上走,我们考虑倍增跳到一个最高的合法的位置进行上述判断即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include <bits/stdc++.h>
using namespace std;

template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
x *= f;
}

template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
r1(t); r1(args...);
}

//#define int long long
const int maxn = 2e5 + 5;
const int maxm = maxn << 1;

int fa[maxn];
int getfa(int x) {
return x == fa[x] ? x : fa[x] = getfa(fa[x]);
}

int head[maxn], cnt;
struct Edge {
int to, next;
}edg[maxn << 2];
void add(int u,int v) {
edg[++ cnt] = (Edge) {v, head[u]}, head[u] = cnt;
}

vector<int> vc1[maxn], vc2[maxn];

void add(vector<int> *vc,int u,int v) {
vc[u].push_back(v);
}

void merge(int u,int v) {
u = getfa(u), v = getfa(v);
if(u != v)
fa[u] = v;
}

int n, m, Q;

int d1l[maxn], d1r[maxn], d2l[maxn], d2r[maxn];
int fid[maxn];
int tot1(0), tot2(0);
const int N = 20;
int fa1[maxn][N + 5], fa2[maxn][N + 5];

void dfs1(int p,int pre) {
d1l[p] = ++ tot1;
fid[tot1] = p;
fa1[p][0] = pre;
for(int i = 1; i <= N; ++ i) fa1[p][i] = fa1[fa1[p][i - 1]][i - 1];
for(int v : vc1[p]) if(v != pre) dfs1(v, p);
d1r[p] = tot1;
}

void dfs2(int p,int pre) {
d2l[p] = ++ tot2;
fa2[p][0] = pre;
for(int i = 1; i <= N; ++ i) fa2[p][i] = fa2[fa2[p][i - 1]][i - 1];
for(int v : vc2[p]) if(v != pre) dfs2(v, p);
d2r[p] = tot2;
}

int rt[maxn];
int lsn[maxn * 25], rsn[maxn * 25], t[maxn * 25];

struct Seg {
#define ls lsn[p]
#define rs rsn[p]
#define mid ((l + r) >> 1)
int tot;
Seg(void) { tot = 0; }
void Insert(int &p,int pre,int l,int r,int pos) {
p = ++ tot; t[p] = t[pre], ls = lsn[pre], rs = rsn[pre];
++ t[p];
if(l == r) return ;
if(pos <= mid) Insert(ls, lsn[pre], l, mid, pos);
else Insert(rs, rsn[pre], mid + 1, r, pos);
}
int ask(int p,int l,int r,int ll,int rr) {
if(!p) return 0;
if(ll <= l && r <= rr) return t[p];
int res(0);
if(ll <= mid) res += ask(ls, l, mid, ll, rr);
if(mid < rr) res += ask(rs, mid + 1, r, ll, rr);
return res;
}
}T;

int findpre1(int x,int v) {
for(int i = N; i >= 0; -- i) if(fa1[x][i] >= v && fa1[x][i] != 0) x = fa1[x][i];
return x;
}

int findpre2(int x,int v) {
for(int i = N; i >= 0; -- i) if(fa2[x][i] <= v && fa2[x][i] != 0) x = fa2[x][i];
return x;
}

signed main() {
// freopen("S.in", "r", stdin);
int i, j;
r1(n, m, Q);
// puts("ZZZZ");
for(i = 1; i <= m; ++ i) {
int u, v; r1(u, v);
++ u, ++ v;
add(u, v), add(v, u);
}
for(i = 1; i <= n; ++ i) fa[i] = i;
for(i = n; i >= 1; -- i) {
for(int j = head[i];j;j = edg[j].next) {
int to = edg[j].to; if(getfa(to) > i) {
add(vc1, i, getfa(to));
merge(to, i);
}
}
}
for(i = 1; i <= n; ++ i) fa[i] = i;
for(i = 1; i <= n; ++ i) {
for(int j = head[i];j;j = edg[j].next) {
int to = edg[j].to; if(getfa(to) < i) {
add(vc2, i, getfa(to));
merge(to, i);
}
}
}
dfs1(1, 0);
dfs2(n, 0);
for(i = 1; i <= n; ++ i)
T.Insert(rt[i], rt[i - 1], 1, n, d2l[fid[i]]);
while(Q --) {
int x, y, l, r;
r1(x, y, l, r);
++ x, ++ y, ++ l, ++ r;
int nx = findpre1(x, l), ny = findpre2(y, r);
// printf("%d %d\n", nx, ny);
int ans = T.ask(rt[d1r[nx]], 1, n, d2l[ny], d2r[ny]) - T.ask(rt[d1l[nx] - 1], 1, n, d2l[ny], d2r[ny]);
// printf("ans = %d\n", ans);
if(ans > 0) puts("1");
else puts("0");
}
return 0;
}

/*
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
*/