POJ1470 RMQLCA

本文介绍了一个具体的LCA(最近公共祖先)问题实例,并提供了一种在线LCA算法的实现细节。该算法通过预处理和查询操作来计算二叉树中两个节点的最近公共祖先,并记录每个节点在查询答案中的出现频率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LCA裸题,要求的是每个点在答案中的出现次数。
试着写了一下在线的LCA算法(结果代码量比起那个离线的暴增。。。)
自己的代码:

#include<cstdio>
#include<cmath>
using namespace std;
int n;
struct e
{
    int t;
    e *n;
    e(int t,e *n):t(t),n(n){}
}*f[901]={};
namespace memo
{
    e *stk[901]={};
    int top=-1;
    void recycle(int p)
    {
        while(p--)
        if(f[p])
        stk[++top]=f[p],f[p]=0;
    }
    e *alloc(int t,e *n)
    {
        if(~top)
        {
            e *rev=stk[top];
            stk[top]=stk[top]->n;
            if(!stk[top]) --top;
            rev->t=t;rev->n=n;
            return rev;
        }
        return new e(t,n);
    }
}
namespace rmq
{
    int c[1801][12],now;
    int dpt[901];
    int first[901];
    inline int min(int a,int b)
    {return dpt[a]<dpt[b]?a:b;}
    void dfs(int x)
    {
        c[++now][0]=x;
        first[x]=now;
        for(e *i=f[x];i;i=i->n)
        {
            dpt[i->t]=dpt[x]+1;
            dfs(i->t);
            c[++now][0]=x;
        }
    }
    void work()
    {
        int m=log2(now);
        for(int i=1;i<=m;i++)
        {
            int k=1<<i;
            for(int j=1;j<=now;j++)
            {
                if(j+k>now) break;
                c[j][i]=min(c[j][i-1],c[j+(k>>1)][i-1]);
            }
        }
    }
    inline int ask(int a,int b)
    {
        if(first[b]<first[a]) a^=b^=a^=b;
        int m=log2(first[b]-first[a]+1);
        return min(c[first[a]][m],c[first[b]-(1<<m)+1][m]);
    }
}
namespace io
{
    int num[901];
    int ct[901]={};
    int tm=0;
    int rt[901];
    void read1()
    {
        int x,z,y;
        for(int i=1;i<=n;i++)
        {
        scanf("%d:(%d)",&x,&z);
        while(z--)
        {
           scanf("%d",&y);
           f[x]=memo::alloc(y,f[x]);
           rt[y]=tm;
        }
        }
    }
    void read2()
    {
           int x=0,y=0,w;
           scanf("%d",&w);
           for(int i=1;i<=w;i++)
           {
           scanf(" (%d %d)",&x,&y);
           int z=rmq::ask(x,y);
           if(ct[z]==tm) num[z]++;
           else ct[z]=tm,num[z]=1;
           }
    }
    void write()
    {
        for(int i=1;i<=n;i++)
        if(num[i]&&ct[i]==tm)
        printf("%d:%d\n",i,num[i]);
    }
}
namespace ctrl
{
    int root()
    {
        for(int i=1;i<=n;i++)
        {
            if(io::rt[i]==io::tm)
            continue;
            return i;
        }
    }
    inline void case_beg()
    {
        rmq::now=0;
        io::tm++;
    }
    inline void tree_build()
    {
        int r=root();
        rmq::dpt[r]=0;
        rmq::dfs(r);
    }
    inline void main_work()
    {
        io::read1();
        tree_build();
        rmq::work();
        io::read2();
        io::write();
    }
    inline void case_end()
    {
        memo::recycle(n);
    }
}
int main()
{
    while(scanf("%d",&n)==1)
    {
        ctrl::case_beg();
        ctrl::main_work();
        ctrl::case_end();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值