Loading

[CEOI 2025] theseus 做题记录

头皮发麻的题目。link

对于一道题目,我们需要观察题目的对象形式有什么特点。对于一道构造题,我们需要思考现在我们想要什么。

对于这题,观察到需要用到一个隐藏条件——点的编号,从编号信息入手。

  • Trial 1:对于一条边 \((u, v)\),根据 \(u,v\) 的大小关系以及边的 label 相当于确定该边的方向。

也就是说,我们给边进行 label 相当于给边进行定向。

注意到 travel 函数是我们自己设计的。为了避免随机化,我们可以固定每个点走的边。

  • Trial 2:为了避免随机游走,travel 的时候可以考虑走出边中编号最小的点。

进一步的,我们确定每个点下一步走到的点后,不难发现这形成了一棵树

这样我们就有了一个思考猜测:通过启发式合并来保证额外走的边数为 \(\mathcal O(\log n)\) 级别的。

先求出每个点的最短路,按照最短路长度分层。

对于某一层的每个点,对于其与上一层的点的连边,显然可以直接定向。

对于同一层内部的点,我们从小到大枚举每个点 \(u\),以及从小到大枚举其连接的点 \(v\),这样枚举的好处是无后效性。

如果 \(u\)\(v\) 已经有父亲了,那么我们可以定向:让有父亲的点连向另一个点。如果都没有父亲,考虑令子树较大的点作为子树较小的点的父亲。

这样就做完了。

点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define uLL unsigned LL
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb emplace_back
#define i128 __int128
const ll maxn = 10010, mod = 998244353, M = 14348907;
const LL inf = 1e18;
template <class T>
void rd(T &x) {
	char ch; ll f = 0;
	while(!isdigit(ch = getchar()))
		if(ch == '-') f = 1;
	x = ch - '0';
	while(isdigit(ch = getchar()))
		x = (x << 1) + (x << 3) + ch - '0';
	if(f) x = -x;
}
ll power(ll a, ll b = mod - 2, ll p = mod) {
	ll s = 1;
	while(b) {
		if(b & 1) s = 1ll * s * a % p;
		a = 1ll * a * a % p, b >>= 1;
	} return s;
}
template <class T1, class T2>
void add(T1 &x, const T2 y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T1, class T2>
void sub(T1 &x, const T2 y) { x = x < y? x + mod - y : x - y; }
template <class T1, class T2>
ll pls(const T1 x, const T2 y) { return x + y >= mod? x + y - mod : x + y; }
template <class T1, class T2>
ll mus(const T1 x, const T2 y) { return x < y? x + mod - y : x - y; }
template <class T1, class T2>
void chkmax(T1 &x, const T2 y) { x = x < y? y : x; }
template <class T1, class T2>
void chkmin(T1 &x, const T2 y) { x = x < y? x : y; }
using namespace std;

ll dis[maxn], q[maxn], l, r, siz[maxn], mn[maxn], d[maxn];
vector <pir> to[maxn]; vector <ll> vec[maxn];

vector <ll> label(ll n, vector <pir> edge, ll t) {
	vector <ll> res(edge.size());
	for(ll i = 1; i <= n; i++) dis[i] = -1;
	q[l = r = 1] = t, dis[t] = 0;
	for(ll i = 0; i < edge.size(); i++) {
		ll u = edge[i].fi, v = edge[i].se;
		to[u].pb(mkp(v, i));
		to[v].pb(mkp(u, i));
	}
	while(l <= r) {
		ll u = q[l++];
		for(auto [v, id]: to[u])
			if(dis[v] == -1) {
				dis[v] = dis[u] + 1;
				q[++r] = v;
			}
	}
	for(ll u = 1; u <= n; u++) vec[dis[u]].pb(u), mn[u] = n + 1, siz[u] = 1;
	for(ll i = n; i; i--) {
		if(vec[i].empty()) continue;
		for(ll u: vec[i])
			for(auto [v, id]: to[u])
				if(dis[v] < dis[u]) res[id] = (u > v), chkmin(mn[u], v);
		sort(vec[i].begin(), vec[i].end());
		for(ll u: vec[i]) {
			sort(to[u].begin(), to[u].end());
			for(auto [v, id]: to[u])
				if(dis[v] == dis[u] && u < v) {
					if(!d[u] && v > mn[u]) siz[d[u] = mn[u]] += siz[u];
					if(!d[v] && u > mn[v]) siz[d[v] = mn[v]] += siz[v];
					if(d[u]) res[id] = 0;
					else if(d[v]) res[id] = 1;
					else {
						if(siz[u] <= siz[v]) res[id] = 0, siz[d[u] = v] += siz[u];
						else res[id] = 1, siz[d[v] = u] += siz[v];
					}
				}
			if(!d[u]) siz[d[u] = mn[u]] += siz[u];
		}
	}
	return res;
}

ll travel(ll n, ll u, vector <pir> e) {
	ll p = n + 1;
	for(auto [v, w]: e)
		if((u < v) ^ w) chkmin(p, v);
	return p;
}
posted @ 2025-09-27 16:25  Sktn0089  阅读(29)  评论(0)    收藏  举报