洛谷 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;
}
posted @ 2022-07-28 21:16  zltzlt  阅读(34)  评论(0编辑  收藏  举报