洛谷3379(LCA模板优化)

problem

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式:

第一行包含三个正整数N、M、S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来N-1行每行包含两个正整数x、y,表示x结点和y结点之间有一条直接连接的边(数据保证可以构成树)。

接下来M行每行包含两个正整数a、b,表示询问a结点和b结点的最近公共祖先。

输出格式:

输出包含M行,每行包含一个正整数,依次为每一个询问的结果。

输入样例#1:

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

输出样例#1:

4
4
1
4
4

思路

数据量可以到500000点 500000询问 提交果然卡常了

1.读入可以用getchar优化(次要)

2.vector较链表在数据量大的时候是慢的(希望有大神可以系统的说明下这个vector的复杂度分析),可以用链式邻接表或者链式前向星模拟邻接表


代码示例(链式邻接表)

#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;//节点数

struct Edge{
    int from,to;
}edges[2*maxn];

const int maxlog=30;
int grand[maxn][maxlog];
//int gdis[maxn][maxlog];
//int gmax[maxn][maxlog];
int depth[maxn];
int n;//结点数
int s;//倍增最大步数
int root;//根节点
int cnt;//边集数

struct EdgeNode{
    int adjvex;//顶点编号
    EdgeNode *next;
};

struct AdList{
    int flag;
    EdgeNode *firstarc;
}G[maxn];

void addEdge(int u,int v){
    //edges[cnt].from=v;
    //edges[cnt].to=head[u];
    //head[u]=cnt++;
    edges[cnt].from=u;
    edges[cnt++].to=v;
    //cout<<"添加"<<edges[cnt-1].from<<" "<<edges[cnt-1].to<<endl;
    EdgeNode *e;
    e=(EdgeNode *)malloc(sizeof(EdgeNode));
    e->adjvex=cnt-1;
    if(G[u].flag==0) G[u].firstarc=NULL,G[u].flag++;
    e->next=G[u].firstarc;
    G[u].firstarc=e;
    //cout<<"目前与节点"<<u<<"连接的一个节点在边集数组的位置"
    //<<G[u].firstarc->adjvex<<endl;
}

void init()
{
    cnt=0;
    memset(grand,0,sizeof(grand));
    //memset(head,-1,sizeof(head));
    memset(depth,0,sizeof(depth));
    //memset(gdis,0,sizeof(gdis));
    //memset(gmax,0,sizeof(gmax));
}

void dfs(int x)//预处理
{
    for(int i=1;i<=s;++i){
        grand[x][i]=grand[grand[x][i-1]][i-1];
        //gdis[x][i]=gdis[x][i-1]+gdis[grand[x][i-1]][i-1];
        //gmax[x][i]=max(gmax[x][i-1],gmax[grand[x][i-1]][i-1]);
        //if(!grand[x][i]) break;
    }
    for(int i=0;G[x].firstarc!=NULL;++i){
            //cout<<edges[G[x].firstarc->adjvex].to<<"TEST"<<endl;
        int tt=edges[G[x].firstarc->adjvex].to;
        G[x].firstarc=G[x].firstarc->next;
        if(tt!=grand[x][0]){
            depth[tt]=depth[x]+1;
            grand[tt][0]=x;
            //gdis[e.to][0]=e.dist;
            //gmax[e.to][0]=e.dist;
            dfs(tt);
        }
    }
}

int lca(int a,int b)//最大值,路径权值和
{
    if(depth[a]>depth[b]) swap(a,b);
    //ans=0;//路径权值和
    //maxx=gmax[b][0];
    //for(int i=s;i>=0;i--)
    //   if(depth[a]<depth[b]&&depth[grand[b][i]]>=depth[a])
    //     b=grand[b][i];

    int dre=depth[b]-depth[a];
    for(int i=s;i>=0;--i){
        if(dre&(1<<i)) b=grand[b][i];
    }
    if(a==b) return a;
    for(int i=s;i>=0;i--)
        if(grand[a][i]!=grand[b][i]){
            //ans+=gdis[a][i],ans+=gdis[b][i];
            //maxx=max(maxx,gmax[a][i]),maxx=max(maxx,gmax[b][i]);
            a=grand[a][i],b=grand[b][i];
        }
        //ans+=gdis[a][0];
        //ans+=gdis[b][0];
        //maxx=max(maxx,gmax[a][0]);
        //maxx=max(maxx,gmax[b][0]);
        return grand[a][0];
}

int read()
{
    char ch='*';
    while(!isdigit(ch=getchar()));//不是数字读掉
    int num=ch-'0';
    while(isdigit(ch=getchar())) num=num*10+ch-'0';
    return num;
}

int main()
{
        init();
        int query,u,v,w;
        scanf("%d %d %d",&n,&query,&root);
        s=floor(log(n+0.0)/log(2.0))+1;
        for(int i=1;i<=n-1;++i){
            u=read();
            v=read();
            addEdge(u,v);
            addEdge(v,u);
        }
        dfs(root);//以root为根结点建树
        for(int i=1;i<=query;++i){
            u=read();
            v=read();
            printf("%d\n",lca(u,v));
        }
        return 0;
}


代码示例(链式向前星)

#include<bits/stdc++.h>
using namespace std;
const int maxn=500010;//节点数

struct Edge{
    int from,to;
}edges[2*maxn];

const int maxlog=30;
int grand[maxn][maxlog];
//int gdis[maxn][maxlog];
//int gmax[maxn][maxlog];
int head[2*maxn];
int depth[maxn];
int n;//结点数
int s;//倍增最大步数
int root;//根节点
int cnt;//边集数

void addEdge(int u,int v){
    edges[cnt].from=v;
    edges[cnt].to=head[u];
    head[u]=cnt++;
}

void init()
{
    cnt=0;
    memset(grand,0,sizeof(grand));
    memset(head,-1,sizeof(head));
    memset(depth,0,sizeof(depth));
    //memset(gdis,0,sizeof(gdis));
    //memset(gmax,0,sizeof(gmax));
}

void dfs(int x)//预处理
{
    for(int i=1;i<=s;++i){
        grand[x][i]=grand[grand[x][i-1]][i-1];
        //gdis[x][i]=gdis[x][i-1]+gdis[grand[x][i-1]][i-1];
        //gmax[x][i]=max(gmax[x][i-1],gmax[grand[x][i-1]][i-1]);
        //if(!grand[x][i]) break;
    }
    for(int i=head[x];i!=-1;i=edges[i].to){
        int tt=edges[i].from;
        if(tt!=grand[x][0]){
            depth[tt]=depth[x]+1;
            grand[tt][0]=x;
            //gdis[e.to][0]=e.dist;
            //gmax[e.to][0]=e.dist;
            dfs(tt);
        }
    }
}

int lca(int a,int b)//最大值,路径权值和
{
    if(depth[a]>depth[b]) swap(a,b);
    //ans=0;//路径权值和
    //maxx=gmax[a][0];
    //for(int i=s;i>=0;i--)
    //   if(depth[a]<depth[b]&&depth[grand[b][i]]>=depth[a])
    //     b=grand[b][i];

    int dre=depth[b]-depth[a];
    for(int i=s;i>=0;--i){
        if(dre&(1<<i)) b=grand[b][i];
    }
    if(a==b) return a;
    for(int i=s;i>=0;i--)
        if(grand[a][i]!=grand[b][i]){
            //ans+=gdis[a][i],ans+=gdis[b][i];
            //maxx=max(maxx,gmax[a][i]),maxx=max(maxx,gmax[b][i]);
            a=grand[a][i],b=grand[b][i];
        }
        //ans+=gdis[a][0];
        //ans+=gdis[b][0];
        //maxx=max(maxx,gmax[a][0]);
        //maxx=max(maxx,gmax[b][0]);
        return grand[a][0];
}

int read()
{
    char ch='*';
    while(!isdigit(ch=getchar()));//不是数字读掉
    int num=ch-'0';
    while(isdigit(ch=getchar())) num=num*10+ch-'0';
    return num;
}

int main()
{
        init();
        int query,u,v,w;
        scanf("%d %d %d",&n,&query,&root);
        s=floor(log(n+0.0)/log(2.0))+1;
        for(int i=1;i<=n-1;++i){
            u=read();
            v=read();
            addEdge(u,v);
            addEdge(v,u);
        }
        dfs(root);//以root为根结点建树
        for(int i=1;i<=query;++i){
            u=read();
            v=read();
            printf("%d\n",lca(u,v));
        }
        return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值