洛谷3379(LCA模板优化)

来源:互联网 发布:有个卖时间的软件 编辑:程序博客网 时间:2024/06/06 20:59

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;}
原创粉丝点击