オイラーツアー
| 投稿者 | ゾビオ |
|---|---|
| 言語 | C++23 |
| 更新日時 (JST) | 2026-10-30 11:52:03 |
| サイズ | 2,239 Byte |
| 参照URL | |
日時はサーバーが記録したものです。更新すると更新日時が変わるので、不都合がある場合は新しく投稿してください。
struct EulerTour
{
ll n;
vvll g;
vll tour; // 訪問順の頂点列(長さ 2N-1)
vll in, out, dep; // in[v]/out[v]: tour 上で v が最初/最後に現れる位置, dep[v]: 深さ
vvll sp; // LCA 用 Sparse Table(tour 上の添字を持つ)
EulerTour(ll n) : n(n), g(n), in(n), out(n), dep(n) {}
void add_edge(ll u, ll v)
{
g[u].push_back(v);
g[v].push_back(u);
}
void build(ll root = 0)
{
tour.clear();
vll par(n, -1), idx(n, 0);
stack<ll> st;
st.push(root);
dep[root] = 0;
in[root] = 0;
tour.push_back(root);
while (st.size())
{
ll v = st.top();
if (idx[v] < (ll)g[v].size())
{
ll to = g[v][idx[v]++];
if (to == par[v]) continue;
par[to] = v;
dep[to] = dep[v] + 1;
in[to] = tour.size();
tour.push_back(to);
st.push(to);
}
else
{
out[v] = (ll)tour.size() - 1;
st.pop();
if (st.size()) tour.push_back(st.top()); // 親に戻る
}
}
build_lca();
}
// tour 上の区間で深さ最小の頂点 = LCA
void build_lca()
{
ll m = tour.size();
ll lg = 1;
while ((1LL << lg) < m) lg++;
sp.assign(lg + 1, vll(m));
rep(i, m) sp[0][i] = i;
reps(k, lg)
{
for (ll i = 0; i + (1LL << k) <= m; i++)
{
ll a = sp[k - 1][i], b = sp[k - 1][i + (1LL << (k - 1))];
sp[k][i] = (dep[tour[a]] < dep[tour[b]] ? a : b);
}
}
}
ll lca(ll u, ll v)
{
ll l = min(in[u], in[v]), r = max(in[u], in[v]) + 1; // [l, r)
ll k = 63 - __builtin_clzll(r - l);
ll a = sp[k][l], b = sp[k][r - (1LL << k)];
return tour[dep[tour[a]] < dep[tour[b]] ? a : b];
}
ll dist(ll u, ll v) { return dep[u] + dep[v] - 2 * dep[lca(u, v)]; }
// u が v の部分木に含まれるか
bool in_subtree(ll v, ll u) { return in[v] <= in[u] && in[u] <= out[v]; }
};