Cod sursa(job #3361489)

Utilizator EricDimiCismaru Eric-Dimitrie EricDimi Data 24 iulie 2026 17:48:24
Problema Lowest Common Ancestor Scor 100
Compilator cpp-64 Status done
Runda Arhiva educationala Marime 4.67 kb
#include <fstream>
#include <vector>

using namespace std;

ifstream f("lca.in");
ofstream g("lca.out");

const int MAX_N = 100000,
          MAX_BLOCK_SIZE = 8,
          MAX_BLOCK_COUNT = ((MAX_N << 1) + MAX_BLOCK_SIZE - 1) / MAX_BLOCK_SIZE,
          MAX_LOG2 = 14; /// log2(MAX_BLOCK_COUNT)

inline int max(int x, int y)
{
    return (x > y) ? x : y;
}

struct Tree
{
    vector<int> adj[MAX_N + 1];
    int euler[MAX_N << 1],
        depth[MAX_N + 1],
        pos[MAX_N + 1];
    int n, q, timer;

    vector<vector<int>> blocks[1 << (MAX_BLOCK_SIZE - 1)];
    int blockMask[MAX_BLOCK_COUNT];
    int st[MAX_LOG2 + 1][MAX_N << 1],
        log2[MAX_N << 1];
    int m, blockSize, blockCount;

    inline int Combine(int x, int y)
    {
        return (depth[euler[x]] < depth[euler[y]]) ? x : y;
    }

    void Read()
    {
        f >> n >> q;
        for(int y = 2; y <= n; y++)
        {
            int x;
            f >> x;
            adj[x].push_back(y);
        }
    }

    void DFS(int node, int dist = 0)
    {
        ++timer;
        euler[timer] = node;
        depth[node] = dist;
        pos[node] = timer;
        for(int child : adj[node])
        {
            DFS(child, dist + 1);
            ++timer;
            euler[timer] = node;
        }
    }

    void EulerTour()
    {
        timer = -1;
        DFS(1);
    }

    void FarachColtonBender()
    {
        m = timer + 1;
        log2[1] = 0;
        for(int i = 2; i <= m; i++)
            log2[i] = log2[i >> 1] + 1;

        blockSize = max(1, (log2[m] >> 1));
        blockCount = (m + blockSize - 1) / blockSize;

        for(int i = 0, pos = 0, blockIdx = 0; i < m; i++, pos++)
        {
            if(pos == blockSize)
            {
                pos = 0;
                blockIdx++;
            }
            if(pos == 0 || Combine(i, st[0][blockIdx]) == i)
                st[0][blockIdx] = i;
            if(pos > 0 && Combine(i - 1, i) == i - 1)
                blockMask[blockIdx] |= 1 << (pos - 1);
        }

        for(int p = 1; p <= log2[blockCount]; p++)
            for(int i = 0; i < blockCount; i++)
            {
                if(i + (1 << (p - 1)) >= blockCount)
                    st[p][i] = st[p - 1][i];
                else
                    st[p][i] = Combine(st[p - 1][i], st[p - 1][i + (1 << (p - 1))]);
            }

        for(int blockIdx = 0; blockIdx < blockCount; blockIdx++)
        {
            int mask = blockMask[blockIdx];
            if(!blocks[mask].empty())
                continue;
            blocks[mask] = vector<vector<int>>(blockSize, vector<int>(blockSize));
            for(int i = 0; i < blockSize; i++)
            {
                blocks[mask][i][i] = i;
                for(int j = i + 1; j < blockSize; j++)
                {
                    blocks[mask][i][j] = blocks[mask][i][j - 1];
                    if(blockIdx * blockSize + j < m)
                    {
                        blocks[mask][i][j] = Combine(blockIdx * blockSize + j,
                                                     blockIdx * blockSize + blocks[mask][i][j]);
                        blocks[mask][i][j] -= blockIdx * blockSize;
                    }
                }
            }
        }
    }

    inline int BlockIdx(int blockIdx, int left, int right)
    {
        return blocks[blockMask[blockIdx]][left][right] + blockIdx * blockSize;
    }

    int LCA(int x, int y)
    {
        int left = pos[x],
            right = pos[y];
        if(left > right)
            swap(left, right);

        int blockLeft = left / blockSize,
            blockRight = right / blockSize;
        int idx = -1;

        if(blockLeft == blockRight)
            idx = BlockIdx(blockLeft, left % blockSize, right % blockSize);
        else
        {
            idx = Combine(BlockIdx(blockLeft, left % blockSize, blockSize - 1),
                          BlockIdx(blockRight, 0, right % blockSize));
            ++blockLeft;
            --blockRight;
            int len = blockRight - blockLeft + 1;
            if(len)
            {
                len = log2[len];
                idx = Combine(idx, st[len][blockLeft]);
                idx = Combine(idx, st[len][blockRight - (1 << len) + 1]);
            }
        }

        return euler[idx];
    }

    void Solve()
    {
        while(q--)
        {
            int x, y;
            f >> x >> y;
            g << LCA(x, y) << '\n';
        }
    }
};
Tree tree;

int main()
{
    tree.Read();
    tree.EulerTour();
    tree.FarachColtonBender();
    tree.Solve();

    f.close();
    g.close();

    return 0;
}