1 条题解

  • 0
    @ 2025-5-3 9:38:30

    暴力搜索

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 5000;
    int n;
    vector<int> e[MAXN + 5];
    // dis[i][j] 存 i->j 的距离
    int dis[MAXN + 5][MAXN + 5];
    // 处理根节点到每个点的距离 dis[root][~]
    void dfs(int u, int fa, int root)
    {
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == fa)
                continue;
            dis[root][v] = dis[root][u] + 1;
            dfs(v, u, root);
        }
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        for (int u = 1; u <= n; u++)
        {
            dis[u][u] = 0;
            dfs(u, 0, u);
        }
        int Q;
        cin >> Q;
        while (Q--)
        {
            int u, v;
            cin >> u >> v;
            cout << dis[u][v] << "\n";
        }
        return 0;
    }
    

    暴力 LCA

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100'000;
    int n;
    vector<int> e[MAXN + 5];
    // f[i] 记录 i 的父节点
    int f[MAXN + 5];
    // dis[i] 求节点 i 的深度
    int dis[MAXN + 5];
    void dfs(int u, int fa)
    {
        f[u] = fa;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == fa)
                continue;
            dis[v] = dis[u] + 1;
            dfs(v, u);
        }
    }
    // lca(u,v) 返回 u,v 的最近公共祖先
    int lca(int u, int v)
    {
        // 保证 u 在上面,v 在下面
        if (dis[v] < dis[u])
            swap(u, v);
        // 拉到同样的深度
        while (dis[v] != dis[u])
            v = f[v];
        // 同步往上走
        while (u != v)
            v = f[v], u = f[u];
        return u;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        dis[1] = 0;
        dfs(1, 0);
        int Q;
        cin >> Q;
        while (Q--)
        {
            int u, v;
            cin >> u >> v;
            int uv = lca(u, v);
            cout << (dis[u] - dis[uv]) + (dis[v] - dis[uv]) << "\n";
        }
        return 0;
    }
    

    预处理 n\sqrt{n} 的祖先

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100000;
    int n, nn;
    vector<int> e[MAXN + 5];
    int fa[MAXN + 5];   // fa[u] 记录 u 的父节点
    int faNN[MAXN + 5]; // faNN[u] 记录 u 的 nn 层祖宗节点
    int dep[MAXN + 5];  // dep[u] 记录 1 为根节点时的深度
    void dfs(int u, int father)
    {
        fa[u] = father;
        for (int v : e[u])
        {
            if (v == father)
                continue;
            dep[v] = dep[u] + 1;
            dfs(v, u);
        }
    }
    int lca(int u, int v)
    {
        if (dep[v] < dep[u])
            swap(u, v);
        while (dep[v] - dep[u] >= nn)
            v = faNN[v];
        while (dep[v] != dep[u])
            v = fa[v];
        while (faNN[v] != faNN[u])
            u = faNN[u], v = faNN[v];
        while (u != v)
            u = fa[u], v = fa[v];
        return u;
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n;
        nn = sqrt(n);
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        dep[1] = 0;
        dfs(1, 0);
        for (int u = 1; u <= n; u++)
        {
            faNN[u] = u;
            for (int i = 1; i <= nn; i++)
                faNN[u] = fa[faNN[u]];
        }
        int Q;
        cin >> Q;
        while (Q--)
        {
            int u, v;
            cin >> u >> v;
            int uv = lca(u, v);
            cout << (dep[u] - dep[uv]) + (dep[v] - dep[uv]) << "\n";
        }
        return 0;
    }
    

    倍增求 lca

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100'000;
    int n;
    vector<int> e[MAXN + 5];
    // f[i][j] 记录 i 的 2^j 级别祖先
    int f[MAXN + 5][25];
    // dis[i] 求节点 i 的深度
    int dis[MAXN + 5];
    void dfs(int u, int fa)
    {
        f[u][0] = fa;
        for (int i = 0; i < e[u].size(); i++)
        {
            int v = e[u][i];
            if (v == fa)
                continue;
            dis[v] = dis[u] + 1;
            dfs(v, u);
        }
    }
    // lca(u,v) 返回 u,v 的最近公共祖先
    int lca(int u, int v)
    {
        // 保证 u 在上面,v 在下面
        if (dis[v] < dis[u])
            swap(u, v);
        // 拉到同样的深度
        for (int j = 20; j >= 0; j--)
            if (dis[v] - dis[u] >= (1 << j))
                v = f[v][j];
        // 初始两点之间是祖孙关系时,拉到同样深度就会变成同一个点
        if (u == v)
            return u;
        // 同步往上走,跳到了 lca 下面一跳位
        for (int j = 20; j >= 0; j--)
            if (f[u][j] != f[v][j])
                u = f[u][j], v = f[v][j];
        return f[u][0];
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        cin >> n;
        for (int i = 1; i <= n - 1; i++)
        {
            int u, v;
            cin >> u >> v;
            e[u].push_back(v);
            e[v].push_back(u);
        }
        dis[1] = 0;
        dfs(1, 0); // 预处理深度 dis[u],以及一级祖先 f[u][0]
    
        // i 的 2^j 级别祖先 = i 的 2^{j-1} 级别祖先的 2^{j-1} 级别祖先
        for (int j = 1; (1LL << j) <= n; j++)
            for (int i = 1; i <= n; i++)
                f[i][j] = f[f[i][j - 1]][j - 1];
    
        int Q;
        cin >> Q;
        while (Q--)
        {
            int u, v;
            cin >> u >> v;
            int uv = lca(u, v);
            cout << (dis[u] - dis[uv]) + (dis[v] - dis[uv]) << "\n";
        }
        return 0;
    }
    

    欧拉序+st表

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN = 100'000;
    int n;
    vector<int> e[MAXN + 5];
    // dis[i] 存 i 的深度
    int dis[MAXN + 5];
    // <深度, 点的编号>
    vector<pair<int, int>> a;
    // pos[i] 存 i 在 a 中第一次出现的下标
    int pos[MAXN + 5];
    void dfs(int u, int fa)
    {
    	a.push_back({dis[u], u});
    	pos[u] = (int)a.size() - 1;
    	for (int i = 0; i < e[u].size(); i++)
    	{
    		int v = e[u][i];
    		if (v == fa)
    			continue;
    		dis[v] = dis[u] + 1;
    		dfs(v, u);
    		a.push_back({dis[u], u});
    	}
    }
    // 处理 a 数组的 st 表
    // st[i][j] 存 a[i] 开始的 2^j 个元素中的最小值下标
    int st[MAXN * 2 + 5][20];
    void initST()
    {
    	int len = a.size();
    	for (int i = 0; i < len; i++)
    		st[i][0] = i;
    	for (int j = 1; (1LL << j) <= len; j++)
    	{
    		for (int i = 0; i + (1LL << j) - 1 < len; i++)
    		{
    			// i 为起点,2^j 长度的最小值下标
    			int L = st[i][j - 1];
    			int R = st[i + (1LL << (j - 1))][j - 1];
    			if (a[L].first < a[R].first)
    				st[i][j] = L;
    			else
    				st[i][j] = R;
    		}
    	}
    }
    int lca(int u, int v)
    {
    	if (pos[u] > pos[v])
    		swap(u, v);
    	// 找到 pos[u]~pos[v] 之间 first 最小的下标
    	int len = pos[v] - pos[u] + 1;
    	int j = log2(len);
    	// pos[u] 开头的 2^j 和 pos[v] 结尾的 2^j 的最小值的最小值
    	int L = st[pos[u]][j];
    	int R = st[pos[v] - (1LL << j) + 1][j];
    	if (a[L].first < a[R].first)
    		return a[L].second;
    	else
    		return a[R].second;
    }
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(0);
    	cin >> n;
    	for (int i = 1; i <= n - 1; i++)
    	{
    		int u, v;
    		cin >> u >> v;
    		e[u].push_back(v);
    		e[v].push_back(u);
    	}
    	dis[1] = 0;
    	dfs(1, 0);
    	initST();
    	int Q;
    	cin >> Q;
    	while (Q--)
    	{
    		int u, v;
    		cin >> u >> v;
    		int uv = lca(u, v);
    		cout << (dis[u] - dis[uv]) + (dis[v] - dis[uv]) << "\n";
    	}
    	return 0;
    }
    

    信息

    ID
    782
    时间
    1000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    97
    已通过
    32
    上传者