树上欧拉序列

树上欧拉序列

树上欧拉序列表示根节点走,按照DFS的方式遍历整颗树,并且把路径上沿途的所有节点都记录下来(包括返回),组成的节点数组称为欧拉序列。欧拉序列满足任意两个连续的节点,其中都存在一条边连接两个节点。

欧拉序列求LCA

O ( n log ⁡ n ) O(n \log n) O(nlogn)时间预处理, O ( 1 ) O(1) O(1)时间查询。

我们定义DSF访问序列:

每一次进入节点和退出节点均需要记录:

序号节点ID深度
1xxxx
2xxxx

则我们查询LCA是在表中两个节点ID之间的最小深度所对应的节点ID。
如果有多个节点ID,任意选取即可。

使用ST表进行静态RMQ查询。

欧拉序列二分

如果我们想查找一条两点路径,其路径长度最大,那么建立欧拉序列在欧拉序列上二分即可。

CF1592D


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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值