头皮发麻的题目。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;
}