JOISC 2022 D1T1 监狱

洛谷传送门

LOJ 传送门

观察可得,若存在合法解,则一定存在一种解,使得每个人都不停顿地从起点走到终点。

因为如果一个人走到一半要停下来等另一个人走,显然这个人也可以选择先走或者等另一个人走完自己再走。

继续观察可得,当且仅当第 \(i, j\) 个人满足以下条件,它们之间的走的先后顺序能被确定:

  • \(s_i\)\(\text{path}(s_j, t_j)\) 上,则 \(i\)\(j\) 先走;
  • \(t_i\)\(\text{path}(s_j, t_j)\) 上,则 \(i\)\(j\) 后走。

建图跑拓扑排序即可。但是朴素建图边数太多了。

考虑把 \(u, v\) 之间的边通过树上的点中转。对于每个点再复制一份,形成 \(x_i, x'_i\)。考虑建图如下:对于第 \(i\) 个人,设 \(s_i \to t_i\) 的路径中,除 \(s_i\) 外走到的第一个点是 \(p\),除 \(t_i\) 外走到的第一个点是 \(q\)

  • 对于 \(u \in \text{path}(s_i, q)\),连边 \(i \to u\)
  • 对于 \(u \in \text{path}(p, t_i)\),连边 \(u' \to i\)
  • 连边 \(t_i \to i, i \to s_i'\)

这样,若 \(s_i\)\(\text{path}(s_j, t_j)\) 上,可以通过 \(i \to s_i' \to j\)\(i\) 到达 \(j\);若 \(t_i\)\(\text{path}(s_j, t_j)\) 上,可以通过 \(j \to t_i \to i\)\(j\) 到达 \(i\)。并且这样建图不会造成多余的影响。

那我们还剩下的问题是,一个点向树上的一条路径上的所有点连边。

考虑树剖后线段树优化建图,边数是 \(O(n \log^2 n)\),就可以通过了。

code
// Problem: P9520 [JOISC 2022 Day1] 监狱
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P9520
// Memory Limit: 1 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<int, int> pii;

const int maxn = 120050;
const int maxm = 3000000;
const int logn = 20;

int n, m, rt1, rt2, ntot, head[maxm], len, to[maxm * 30], nxt[maxm * 30], id[maxn][2], di[maxn], ind[maxm];
pii a[maxn];
vector<int> G[maxn];

inline void add_edge(int u, int v) {
	to[++len] = v;
	nxt[len] = head[u];
	++ind[v];
	head[u] = len;
}

int ls[maxm], rs[maxm], si[maxm], stot;
int fa[maxn], dep[maxn], son[maxn], sz[maxn], top[maxn], dfn[maxn], times, rnk[maxn];
int f[maxn][logn];

void build(int &p, int &q, int l, int r) {
	p = ++stot;
	q = ++stot;
	si[p] = ++ntot;
	si[q] = ++ntot;
	if (l == r) {
		add_edge(si[p], id[rnk[l]][0]);
		add_edge(id[rnk[l]][1], si[q]);
		return;
	}
	int mid = (l + r) >> 1;
	build(ls[p], ls[q], l, mid);
	build(rs[p], rs[q], mid + 1, r);
	add_edge(si[p], si[ls[p]]);
	add_edge(si[p], si[rs[p]]);
	add_edge(si[ls[q]], si[q]);
	add_edge(si[rs[q]], si[q]);
}

void update1(int rt, int l, int r, int ql, int qr, int u) {
	if (ql <= l && r <= qr) {
		add_edge(u, si[rt]);
		return;
	}
	int mid = (l + r) >> 1;
	if (ql <= mid) {
		update1(ls[rt], l, mid, ql, qr, u);
	}
	if (qr > mid) {
		update1(rs[rt], mid + 1, r, ql, qr, u);
	}
}

void update2(int rt, int l, int r, int ql, int qr, int u) {
	if (ql <= l && r <= qr) {
		add_edge(si[rt], u);
		return;
	}
	int mid = (l + r) >> 1;
	if (ql <= mid) {
		update2(ls[rt], l, mid, ql, qr, u);
	}
	if (qr > mid) {
		update2(rs[rt], mid + 1, r, ql, qr, u);
	}
}

int dfs(int u, int f, int d) {
	fa[u] = f;
	sz[u] = 1;
	dep[u] = d;
	int mx = -1;
	for (int v : G[u]) {
		if (v == f) {
			continue;
		}
		::f[v][0] = u;
		sz[u] += dfs(v, u, d + 1);
		if (sz[v] > mx) {
			son[u] = v;
			mx = sz[v];
		}
	}
	return sz[u];
}

void dfs2(int u, int tp) {
	top[u] = tp;
	dfn[u] = ++times;
	rnk[times] = u;
	if (!son[u]) {
		return;
	}
	dfs2(son[u], tp);
	for (int v : G[u]) {
		if (!dfn[v]) {
			dfs2(v, v);
		}
	}
}

inline int qlca(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	return x;
}

inline void treeadd1(int x, int y, int u) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		update1(rt1, 1, n, dfn[top[x]], dfn[x], u);
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	update1(rt1, 1, n, dfn[x], dfn[y], u);
}

inline void treeadd2(int x, int y, int u) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) {
			swap(x, y);
		}
		update2(rt2, 1, n, dfn[top[x]], dfn[x], u);
		x = fa[top[x]];
	}
	if (dep[x] > dep[y]) {
		swap(x, y);
	}
	update2(rt2, 1, n, dfn[x], dfn[y], u);
}

inline int jump(int x, int k) {
	for (int i = 0; i <= 18; ++i) {
		if (k & (1 << i)) {
			x = f[x][i];
		}
	}
	return x;
}

void solve() {
	scanf("%d", &n);
	for (int i = 1; i <= ntot; ++i) {
		head[i] = ind[i] = 0;
	}
	len = ntot = stot = times = 0;
	for (int i = 1; i <= n; ++i) {
		fa[i] = dep[i] = son[i] = sz[i] = top[i] = dfn[i] = 0;
		vector<int>().swap(G[i]);
	}
	for (int i = 1, u, v; i < n; ++i) {
		scanf("%d%d", &u, &v);
		G[u].pb(v);
		G[v].pb(u);
	}
	for (int i = 1; i <= n; ++i) {
		id[i][0] = ++ntot;
		id[i][1] = ++ntot;
	}
	dfs(1, -1, 1);
	dfs2(1, 1);
	build(rt1, rt2, 1, n);
	scanf("%d", &m);
	for (int i = 1; i <= m; ++i) {
		scanf("%d%d", &a[i].fst, &a[i].scd);
		di[i] = ++ntot;
	}
	for (int j = 1; j <= 18; ++j) {
		for (int i = 1; i <= n; ++i) {
			f[i][j] = f[f[i][j - 1]][j - 1];
		}
	}
	for (int i = 1; i <= m; ++i) {
		int x = a[i].fst, y = a[i].scd;
		int xx = -1, yy = -1;
		int lca = qlca(x, y);
		if (x == lca) {
			xx = jump(y, dep[y] - dep[lca] - 1);
		} else {
			xx = fa[x];
		}
		if (y == lca) {
			yy = jump(x, dep[x] - dep[lca] - 1);
		} else {
			yy = fa[y];
		}
		treeadd1(x, yy, di[i]);
		treeadd2(xx, y, di[i]);
		add_edge(id[y][0], di[i]);
		add_edge(di[i], id[x][1]);
	}
	static int q[maxm];
	int hd = 1, tl = 0;
	for (int i = 1; i <= ntot; ++i) {
		if (!ind[i]) {
			q[++tl] = i;
		}
	}
	while (hd <= tl) {
		int u = q[hd++];
		for (int i = head[u]; i; i = nxt[i]) {
			int v = to[i];
			if (!(--ind[v])) {
				q[++tl] = v;
			}
		}
	}
	for (int i = 1; i <= ntot; ++i) {
		if (ind[i]) {
			puts("No");
			return;
		}
	}
	puts("Yes");
}

int main() {
	int T = 1;
	scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

鲜花:

调了一下午。

posted @ 2023-09-08 19:13  zltzlt  阅读(58)  评论(0编辑  收藏  举报