洛谷 P4899 [IOI2018] werewolf 狼人
思路
考虑按边权从小到大和从大到小排序,建两棵 \(\mathrm{Kruskal}\) 重构树。根据 \(\min\) 和 \(\max\) 倍增到相应的祖先结点,问题就转化成了两棵子树交。记 \(a,b\) 分别为两棵树的 \(\mathrm{dfs}\) 序,则问题为满足 \(i \in [l_1,r_1]\),\(j \in [l_2,r_2]\) 且 \(a_i = b_j\) 的 \(i,j\) 数量。设 \(c_i\) 为 \(b_i\) 在 \(a\) 中出现的下标,问题即求 \(c_i \in [l_1,r_1]\),\(i \in [l_2,r_2]\) 的 \(i\) 的数量。二维偏序,在线主席树求解即可。
代码
code
/*
p_b_p_b txdy
AThousandSuns txdy
Wu_Ren txdy
Appleblue17 txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;
const int maxn = 400100;
const int maxm = 10000000;
const int logn = 20;
const int inf = 0x3f3f3f3f;
int n, m, q, fa[maxn], val[maxn][2];
int f[maxn][logn], g[maxn][logn];
int mnd[maxn][2], mxd[maxn][2], dfn[maxn][2], rnk[maxn][2], times;
pii gg[maxn];
int ntot, rt[maxn], ls[maxm], rs[maxm], tree[maxm];
bool cmp(pii a, pii b) {
return min(a.fst, a.scd) > min(b.fst, b.scd);
}
bool cmp2(pii a, pii b) {
return max(a.fst, a.scd) < max(b.fst, b.scd);
}
struct graph {
int head[maxn], len, to[maxn], nxt[maxn];
void add_edge(int u, int v) {
to[++len] = v;
nxt[len] = head[u];
head[u] = len;
}
} G, T;
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void dfs(int u, int fa) {
mnd[u][0] = inf;
mxd[u][0] = -inf;
bool flag = 1;
for (int i = G.head[u]; i; i = G.nxt[i]) {
int v = G.to[i];
if (v == fa) {
continue;
}
flag = 0;
f[v][0] = u;
dfs(v, u);
mnd[u][0] = min(mnd[u][0], mnd[v][0]);
mxd[u][0] = max(mxd[u][0], mxd[v][0]);
}
if (flag) {
mnd[u][0] = mxd[u][0] = dfn[u][0] = ++times;
rnk[times][0] = u;
}
}
void dfs2(int u, int fa) {
mnd[u][1] = inf;
mxd[u][1] = -inf;
bool flag = 1;
for (int i = T.head[u]; i; i = T.nxt[i]) {
int v = T.to[i];
if (v == fa) {
continue;
}
flag = 0;
g[v][0] = u;
dfs2(v, u);
mnd[u][1] = min(mnd[u][1], mnd[v][1]);
mxd[u][1] = max(mxd[u][1], mxd[v][1]);
}
if (flag) {
mnd[u][1] = mxd[u][1] = dfn[u][1] = ++times;
rnk[times][1] = u;
}
}
int build(int l, int r) {
int root = ++ntot;
if (l == r) {
return root;
}
int mid = (l + r) >> 1;
ls[root] = build(l, mid);
rs[root] = build(mid + 1, r);
return root;
}
int update(int rt, int l, int r, int x) {
int dir = ++ntot;
ls[dir] = ls[rt];
rs[dir] = rs[rt];
tree[dir] = tree[rt] + 1;
if (l == r) {
return dir;
}
int mid = (l + r) >> 1;
if (x <= mid) {
ls[dir] = update(ls[dir], l, mid, x);
} else {
rs[dir] = update(rs[dir], mid + 1, r, x);
}
return dir;
}
int query(int u, int v, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
return tree[v] - tree[u];
}
int mid = (l + r) >> 1, res = 0;
if (ql <= mid) {
res += query(ls[u], ls[v], l, mid, ql, qr);
}
if (qr > mid) {
res += query(rs[u], rs[v], mid + 1, r, ql, qr);
}
return res;
}
void solve() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &gg[i].fst, &gg[i].scd);
++gg[i].fst;
++gg[i].scd;
}
int totn1 = n, totn2 = n;
sort(gg + 1, gg + m + 1, cmp);
for (int i = 1; i <= n * 2; ++i) {
fa[i] = i;
}
for (int i = 1; i <= m; ++i) {
int u = gg[i].fst, v = gg[i].scd, d = min(gg[i].fst, gg[i].scd);
int x = find(u), y = find(v);
if (x != y) {
int z = ++totn1;
fa[x] = fa[y] = z;
G.add_edge(z, x);
G.add_edge(z, y);
val[z][0] = d;
}
}
sort(gg + 1, gg + m + 1, cmp2);
for (int i = 1; i <= n * 2; ++i) {
fa[i] = i;
}
for (int i = 1; i <= m; ++i) {
int u = gg[i].fst, v = gg[i].scd, d = max(gg[i].fst, gg[i].scd);
int x = find(u), y = find(v);
if (x != y) {
int z = ++totn2;
fa[x] = fa[y] = z;
T.add_edge(z, x);
T.add_edge(z, y);
val[z][1] = d;
}
}
times = 0;
dfs(totn1, -1);
times = 0;
dfs2(totn2, -1);
for (int j = 1; (1 << j) <= totn1; ++j) {
for (int i = 1; i <= totn1; ++i) {
f[i][j] = f[f[i][j - 1]][j - 1];
}
}
for (int j = 1; (1 << j) <= totn2; ++j) {
for (int i = 1; i <= totn2; ++i) {
g[i][j] = g[g[i][j - 1]][j - 1];
}
}
rt[0] = build(1, n);
for (int i = 1; i <= n; ++i) {
// printf("%d ", rnk[dfn[i][1]][0]);
rt[i] = update(rt[i - 1], 1, n, dfn[rnk[i][1]][0]);
}
// putchar('\n');
while (q--) {
int x, y, l, r;
scanf("%d%d%d%d", &x, &y, &l, &r);
++x;
++y;
++l;
++r;
for (int i = 19; ~i; --i) {
if (f[x][i] && val[f[x][i]][0] >= l) {
x = f[x][i];
}
}
for (int i = 19; ~i; --i) {
if (g[y][i] && val[g[y][i]][1] <= r) {
y = g[y][i];
}
}
// printf("%d %d %d %d\n", mnd[x][0], mxd[x][0], mnd[y][1], mxd[y][1]);
int res = query(rt[mnd[y][1] - 1], rt[mxd[y][1]], 1, n, mnd[x][0], mxd[x][0]);
// printf("%d\n", res);
puts(res > 0 ? "1" : "0");
/*
int res = 0;
for (int i = mnd[y][1]; i <= mxd[y][1]; ++i) {
int u = rnk[i][1];
if (mnd[x][0] <= dfn[u][0] && dfn[u][0] <= mxd[x][0]) {
++res;
}
}
puts(res > 0 ? "1" : "0");
*/
}
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}