一道很棒的KD树分治题.

Description

给一张图, 每条边有$2$种边权.

每次询问两个点$𝑎$和$𝑏$,要求找一条$𝑎$到$𝑏$的路径,使得这条路径上第$k$种边权的最大值恰好是$𝑐_𝑘​$.

$𝑛,𝑚\le 50000​$.

Solution

对于一个询问, 实际上我们要找到是否存在每种属性分别小于等于一个阈值的边, 能否把两个点联通, 且存在最大值恰好为阈值. 这是可以用并查集维护的.

由于属性有两个限制, 我们可以用KD树替换线段树, 做到二维询问. 先把询问离线建树, 把所有边插入对应范围KD树后, 遍历这棵树, 启发式合并栈序撤销并查集, 即可依次求出答案.

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
#include <bits/stdc++.h>

using namespace std;

typedef unsigned short ushort;
typedef vector<int>::iterator vit;

const int maxn = 1e+5 + 5;
const int inf = 0x3f3f3f3f;

int d;
struct Point {
int x, y;
Point(int x_ = 0, int y_ = 0) : x(x_), y(y_){}
inline bool operator < (const Point &rhs) const {
if (d) return (x == rhs.x) ? y < rhs.y : x < rhs.x;
return (y == rhs.y) ? x < rhs.x : y < rhs.y;
}
};
struct Option {
ushort u, v; Point p; int id;
inline bool operator < (const Option &rhs) const {
return p < rhs.p;
}
}e[maxn], que[maxn];
struct KDNode {
int ch[2];
Point l, r;
KDNode() {
ch[0] = ch[1] = 0;
}
KDNode(Point P) : l(P), r(P){ ch[0] = ch[1] = 0; }
}t[maxn * 10];
struct StackNode {
int pt, mxa, mxb; bool same;
}stk[maxn];
int n, m, q, rt, ptr, top;
bool ans[maxn];
ushort f[maxn], sz[maxn]; int mxa[maxn], mxb[maxn];
vector<int> v[maxn * 10];

#define ls t[cur].ch[0]
#define rs t[cur].ch[1]
inline int rd() {
register int x = 0, c = getchar();
while (!isdigit(c)) c = getchar();
while (isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
return x;
}
void init() {
for (int i = 1; i <= n; ++i) f[i] = i, sz[i] = 1, mxa[i] = mxb[i] = -inf;
}
inline void pushup(int cur) {
t[cur].l.x = min(t[ls].l.x, t[rs].l.x);
t[cur].l.y = min(t[ls].l.y, t[rs].l.y);
t[cur].r.x = max(t[ls].r.x, t[rs].r.x);
t[cur].r.y = max(t[ls].r.y, t[rs].r.y);
}
int build(int l, int r, int dim) {
//if (l > r) return 0;
int cur = ++ptr, mid = (l + r) >> 1;
if (l == r) {
t[cur] = KDNode(que[l].p);
return cur;
}
d = dim;
nth_element(que + l, que + mid, que + r + 1);
ls = build(l, mid, dim ^ 1);
rs = build(mid + 1, r, dim ^ 1);
pushup(cur);
return cur;
}
void update(int cur, int id) {
if (t[cur].r.x < e[id].p.x || t[cur].r.y < e[id].p.y) return;
if (t[cur].l.x >= e[id].p.x && t[cur].l.y >= e[id].p.y) {
v[cur].push_back(id);
return;
}
//int mid = (l + r) >> 1;
update(ls, id); update(rs, id);
}
int find(int x) {
return (f[x] == x) ? x : find(f[x]);
}
void dfs(int cur, int l, int r) {
int lim = top;
for (vit i = v[cur].begin(); i != v[cur].end(); ++i) {
int fx = find(e[*i].u), fy = find(e[*i].v);
//if (fx == fy) continue;
int frm, to;
if (sz[fx] < sz[fy]) frm = fx, to = fy;
else frm = fy, to = fx;
stk[++top] = (StackNode){frm, mxa[to], mxb[to]};
// warning!!!!
// the max infomation memorized in stack(mxa, mxb) is to recovery
// its father's information! not itself!;
if (fx == fy) {
stk[top].same = true;
mxa[fx] = max(mxa[fx], e[*i].p.x);
mxb[fx] = max(mxb[fx], e[*i].p.y);
continue;
}
sz[to] += sz[frm];
f[frm] = to;
mxa[to] = max(max(mxa[to], mxa[frm]), e[*i].p.x);
mxb[to] = max(max(mxb[to], mxb[frm]), e[*i].p.y);
}
if (l == r) {
int fu = find(que[l].u), fv = find(que[l].v);
//printf("%d %d %d %d %d\n", que[l].id, fu, fv, mxa[fu], mxb[fu]);
ans[que[l].id] = (fu == fv && mxa[fu] == que[l].p.x && mxb[fu] == que[l].p.y);
} else {
int mid = (l + r) >> 1;
dfs(ls, l, mid); dfs(rs, mid + 1, r);
}
while (top > lim) {
StackNode now = stk[top--];
int t = now.pt;
mxa[f[t]] = now.mxa;
mxb[f[t]] = now.mxb;
if (now.same) continue;
sz[f[t]] -= sz[t];
f[t] = t;
}
}

int main() {
n = rd(); m = rd();
init();
for (register int i = 1; i <= m; ++i) {
e[i].u = rd(); e[i].v = rd();
e[i].p.x = rd(); e[i].p.y = rd();
}
q = rd();
for (register int i = 1; i <= q; ++i) {
que[i].u = rd(); que[i].v = rd();
que[i].p.x = rd(); que[i].p.y = rd();
que[i].id = i;
}
rt = build(1, q, 0);
for (int i = 1; i <= m; ++i) update(rt, i);
dfs(rt, 1, q);
for (int i = 1; i <= q; ++i)
puts(ans[i] ? "Yes" : "No");
return 0;
}