树上欧拉序列
树上欧拉序列表示根节点走,按照DFS的方式遍历整颗树,并且把路径上沿途的所有节点都记录下来(包括返回),组成的节点数组称为欧拉序列。欧拉序列满足任意两个连续的节点,其中都存在一条边连接两个节点。
欧拉序列求LCA
O ( n log n ) O(n \log n) O(nlogn)时间预处理, O ( 1 ) O(1) O(1)时间查询。
我们定义DSF访问序列:
每一次进入节点和退出节点均需要记录:
序号 | 节点ID | 深度 |
---|---|---|
1 | xx | xx |
2 | xx | xx |
则我们查询LCA是在表中两个节点ID之间的最小深度所对应的节点ID。
如果有多个节点ID,任意选取即可。
使用ST表进行静态RMQ查询。
欧拉序列二分
如果我们想查找一条两点路径,其路径长度最大,那么建立欧拉序列在欧拉序列上二分即可。
struct Edge
{
int to;
int nxt;
} e[2005];
int head[1005];
int tot = 0;
void add(int u, int v)
{
tot++;
e[tot].to = v;
e[tot].nxt = head[u];
head[u] = tot;
}
vector<int> path;
void EulerTravel(int u, int r)
{
path.push_back(u);
for (int ne = head[u]; ne; ne = e[ne].nxt)
{
int to = e[ne].to;
if (to == r)
continue;
EulerTravel(to, u);
path.push_back(u);
}
}
void solve()
{
int n;
cin >> n;
for (int i = 0; i < n - 1; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
EulerTravel(1, 1);
int l = 0;
int r = path.size() - 1;
set<int> st;
for (int i = 0; i < path.size(); i++)
{
st.insert(path[i]);
}
cout << "? " << st.size() << " ";
for (int i : st)
{
cout << i << " ";
}
cout << endl;
cout.flush();
int mx;
cin >> mx;
while (l < r - 1)
{
int mid = (l + r) >> 1;
set<int> stt;
for (int i = l; i <= mid; i++)
{
stt.insert(path[i]);
}
cout << "? " << stt.size() << " ";
for (int k : stt)
cout << k << " ";
cout << endl;
cout.flush();
int val;
cin >> val;
if (val == mx)
{
r = mid;
}
else
{
l = mid;
}
}
cout << "! " << path[l] << " " << path[r] << endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
solve();
return 0;
}