HDU 5266 HDU 2586 LCA问题

来源:互联网 发布:淘宝好评返现处罚规则 编辑:程序博客网 时间:2024/06/11 22:01

首先来一发基于RMQ-ST的LCA模板

#pragma comment(linker, "/STACK:1024000000,1024000000")#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<iostream>#include<algorithm>#include<sstream>#include<fstream>#include<vector>#include<map>#include<stack>#include<list>#include<set>#include<queue>#define LL long long#define lson l,m,rt<<1#define rson m+1,r,rt<<1 | 1using namespace std;const int maxn=100005,inf=1<<29;int dir[][2]={ {0,1},{-1,0},{0,-1},{1,0},{-1,1},{-1,-1},{1,-1},{1,1}};int n,m,t,root;/*给定一棵树,求某两个树节点的最近公共祖先。算法复杂度O(n+(2*n-1)*log(2*n-1)+m)o(n)为DFS遍历复杂度,o((2*n-1)*log(2*n-1))为RMQ-ST初始化复杂度有m个查询,对于每个查询都是o(1),故查询总复杂度为o(m)n一般较大,则递归深度达,必然会爆栈故需要用黑科技#pragma comment(linker, "/STACK:1024000000,1024000000")调整虚拟栈的大小,但是这条代码只能在C++编译器中编译,故需要用C++提交而C++对STL支持比较差,故用vector建图会导致TLE,所以用静态链表建图*/struct node{    int to,next,w;}edge[maxn];//存放邻接表的边int head[maxn],cnt;//邻接表表头和总边数void add(int from,int to,int w)//往邻接表加边{    edge[cnt].to=to;    edge[cnt].w=w;    edge[cnt].next=head[from];    head[from]=cnt++;}int vs[maxn<<1];//DFS访问的顺序int depth[maxn<<1];//节点的深度int id[maxn];//各个顶点在vs中首次出现的下标int dp[maxn<<1][20];//维护RMQ-STvoid init_rmq_index(int a[],int n)//基于寻找下标的RMQ-ST初始化{    for(int i=0;i<n;i++) dp[i][0]=i;    for(int j=1;(1<<j)<=n;j++)        for(int i=0;i+(1<<j)-1<n;i++)        if(a[dp[i][j-1]]<a[dp[i+(1<<(j-1))][j-1]]) dp[i][j]=dp[i][j-1];        else dp[i][j]=dp[i+(1<<(j-1))][j-1];}int rmq_index(int a[],int l,int r)//RMQ-ST查询{    int k=(int)(log(1.0*(r-l+1))/log(2.0));    if(a[dp[l][k]]<a[dp[r-(1<<k)+1][k]]) return dp[l][k];    else return dp[r-(1<<k)+1][k];}void dfs(int v,int p,int d,int &k)//获取顶点DFS序,以及深度,首次出现位置{    id[v]=k;    vs[k]=v;    depth[k++]=d;    for(int i=head[v];i!=-1;i=edge[i].next)    {        int to=edge[i].to;        if(to!=p)        {            dfs(to,v,d+1,k);            vs[k]=v;            depth[k++]=d;        }    }}void init(int V)//初始化LCA{    int k=0;    dfs(root,-1,0,k);    init_rmq_index(depth,V*2-1);}int LCA(int u,int v)//LCA查询{    int l=min(id[u],id[v]),r=max(id[u],id[v]);    int pos=rmq_index(depth,l,r);    return vs[pos];}int main(){    //freopen("HDU2586_in.txt","r",stdin);    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&m);        cnt=0;        memset(head,-1,sizeof(head));        for(int i=0;i<n-1;i++)        {            int x,y,w;            scanf("%d%d%d",&x,&y,&w);            add(x,y,w);            add(y,x,w);        }        root=1;        init(n);        int l,r;        for(int i=0;i<m;i++)        {            scanf("%d%d",&l,&r);            printf("%d\n",LCA(l,r));        }    }    return 0;}

题目链接:2586

题目大意,给出一棵根为1的树,然后给出一些询问,每个询问包含两个节点,求两个节点的最短距离。

当然这题数据比较小可以用DFS水过~

不过用LCA来解决就比较高效了。首先预处理出每个节点分别都根节点的距离。

然后对于某两个节点a,b,可以求出最近公共祖先,所以有d=dis[a]+dis[b]-2*dis[LCA(a,b)];

算是对模板的一个应用吧。

另:网上都是一些抄袭狗啊,我就没见到用基于RMQ-ST来解决LCA的,都是用跳表或者是离线搞的。

下面是AC代码

#pragma comment(linker, "/STACK:1024000000,1024000000")#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<iostream>#include<algorithm>#include<sstream>#include<fstream>#include<vector>#include<map>#include<stack>#include<list>#include<set>#include<queue>#define LL long long#define lson l,m,rt<<1#define rson m+1,r,rt<<1 | 1using namespace std;const int maxn=100005,inf=1<<29;int dir[][2]={ {0,1},{-1,0},{0,-1},{1,0},{-1,1},{-1,-1},{1,-1},{1,1}};int n,m,t,root;struct node{    int to,next,w;}edge[maxn];int head[maxn],cnt;void add(int from,int to,int w){    edge[cnt].to=to;    edge[cnt].w=w;    edge[cnt].next=head[from];    head[from]=cnt++;}int vs[maxn<<1];//DFS访问的顺序int depth[maxn<<1];//节点的深度int id[maxn];//各个顶点在vs中首次出现的下标int dp[maxn<<1][20],dis[maxn];void init_rmq_index(int a[],int n){    for(int i=0;i<n;i++) dp[i][0]=i;    for(int j=1;(1<<j)<=n;j++)        for(int i=0;i+(1<<j)-1<n;i++)        if(a[dp[i][j-1]]<a[dp[i+(1<<(j-1))][j-1]]) dp[i][j]=dp[i][j-1];        else dp[i][j]=dp[i+(1<<(j-1))][j-1];}int rmq_index(int a[],int l,int r){    int k=(int)(log(1.0*(r-l+1))/log(2.0));    if(a[dp[l][k]]<a[dp[r-(1<<k)+1][k]]) return dp[l][k];    else return dp[r-(1<<k)+1][k];}void dfs(int v,int p,int d,int &k){    id[v]=k;    vs[k]=v;    depth[k++]=d;    for(int i=head[v];i!=-1;i=edge[i].next)    {        int to=edge[i].to;        if(to!=p)        {            dis[to]=dis[v]+edge[i].w;            dfs(to,v,d+1,k);            vs[k]=v;            depth[k++]=d;        }    }}void init(int V){    int k=0;    for(int i=0;i<=V;i++) dis[i]=-1;    //memset(dis,-1,sizeof(dis));    dis[root]=0;    dfs(root,-1,0,k);    init_rmq_index(depth,V*2-1);}int LCA(int u,int v){    int l=min(id[u],id[v]),r=max(id[u],id[v]);    int pos=rmq_index(depth,l,r);    return vs[pos];}int main(){    //freopen("HDU2586_in.txt","r",stdin);    scanf("%d",&t);    while(t--)    {        scanf("%d%d",&n,&m);        cnt=0;        memset(head,-1,sizeof(head));        for(int i=0;i<n-1;i++)        {            int x,y,w;            scanf("%d%d%d",&x,&y,&w);            add(x,y,w);            add(y,x,w);           // G[x].push_back(node(y,w));            //G[y].push_back(node(x,w));           // printf("yes\n");        }        root=1;        init(n);/* for(int i=0;i<2*n-1;i++)            printf("%d ",vs[i]);        printf("\n");        for(int i=0;i<2*n-1;i++)            printf("%d ",depth[i]);        printf("\n");        for(int i=1;i<=n;i++)            printf("%d ",id[i]);        printf("\n");        for(int i=1;i<=n;i++)            printf("%d ",dis[i]);        printf("\n");*/        int l,r;        for(int i=0;i<m;i++)        {            scanf("%d%d",&l,&r);            //printf("LCA(%d,%d) = %d\n",l,r,LCA(l,r));            printf("%d\n",dis[l]+dis[r]-2*dis[LCA(l,r)]);        }    }    return 0;}
2:考虑dfs序,通过在简单的证明可知L~R的LCA为L~R中dfs序较小的那个位置与dfs序较大的那个位置的LCA。因此只要通过st表处理L~R最大dfs序与最小dfs序的编号即可。

求L~R中dfs序较小的那个位置与dfs序较大的那个位置,如果还是用ST表维护,那非常耗内存,故可以牺牲时间换空间,用线段树来维护RMQ。这样内存的使用就比较充足了。

下面是AC代码:

#pragma comment(linker, "/STACK:1024000000,1024000000")#include<cstdio>#include<cstring>#include<cmath>#include<cstdlib>#include<iostream>#include<algorithm>#include<sstream>#include<fstream>#include<vector>#include<map>#include<stack>#include<list>#include<set>#include<queue>#define LL long long#define lson l,m,rt<<1#define rson m+1,r,rt<<1 | 1using namespace std;const int maxn=300005,inf=1<<29;int n,m,t,root,minl,maxr;struct node{    int to,next;//,w;}edge[maxn<<1];int head[maxn],cnt;void add(int from,int to){    edge[cnt].to=to;    //edge[cnt].w=w;    edge[cnt].next=head[from];    head[from]=cnt++;}int vs[maxn<<1];//DFS访问的顺序int depth[maxn<<1];//节点的深度int id[maxn];//各个顶点在vs中首次出现的下标int dp[maxn<<1][20],Min[maxn<<2],Max[maxn<<2];void init_rmq_index(int a[],int n){    for(int i=0;i<n;i++) dp[i][0]=i;    for(int j=1;(1<<j)<=n;j++)        for(int i=0;i+(1<<j)-1<n;i++)        if(a[dp[i][j-1]]<a[dp[i+(1<<(j-1))][j-1]]) dp[i][j]=dp[i][j-1];        else dp[i][j]=dp[i+(1<<(j-1))][j-1];}int rmq_index(int a[],int l,int r){    int k=(int)(log(1.0*(r-l+1))/log(2.0));    if(a[dp[l][k]]<a[dp[r-(1<<k)+1][k]]) return dp[l][k];    else return dp[r-(1<<k)+1][k];}void PushUp(int rt){    Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);    Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);}void build(int l,int r,int rt){    if(l==r)    {        Min[rt]=Max[rt]=id[l];        return ;    }    int m=(l+r)>>1;    build(lson);    build(rson);    PushUp(rt);}void Query(int L,int R,int l,int r,int rt){    if(L<=l&&r<=R)    {        minl=min(minl,Min[rt]);        maxr=max(maxr,Max[rt]);        return ;    }    int m=(l+r)>>1,ans=1<<30;    if(L<=m) Query(L,R,lson);    if(R>m) Query(L,R,rson);    //return ans;}void dfs(int v,int p,int d,int &k){    id[v]=k;    vs[k]=v;    depth[k++]=d;    for(int i=head[v];i!=-1;i=edge[i].next)        if(edge[i].to!=p)        {            dfs(edge[i].to,v,d+1,k);            vs[k]=v;            depth[k++]=d;        }}void init(int V){    int k=0;    dfs(root,-1,0,k);    init_rmq_index(depth,V*2-1);    build(1,V,1);}int LCA(int u,int v){   // int l,r;    //rmq(u,v,l,r);    int pos=rmq_index(depth,u,v);    return vs[pos];}int main(){    // freopen("HDU5226_in.txt","r",stdin);  //  scanf("%d",&t);    while(~scanf("%d",&n))    {        cnt=0;        memset(head,-1,sizeof(head));        for(int i=0;i<n-1;i++)        {            int x,y;            scanf("%d%d",&x,&y);            add(x,y);            add(y,x);            //G[x].push_back(y);            //G[y].push_back(x);        }        root=1;        init(n);        int l,r;        scanf("%d",&m);        while(m--)        {            scanf("%d%d",&l,&r);            minl=1<<30,maxr=-1;            Query(l,r,1,n,1);            printf("%d\n",LCA(minl,maxr));        }    }    return 0;}


0 0
原创粉丝点击