HDU 5266 pog loves szh III (LAC)

问题描述

pog在与szh玩游戏,首先pog在纸上画了一棵有根树,这里我们定义1为这棵树的根,然后szh在这棵树中选了若干个点,想让pog帮忙找找这些点的最近公共祖先在哪里,一个点为S的最近公共祖先当且仅当以该点为根的子树包含S中的所有点,且该点深度最大。然而,这个问题是十分困难的,出于szh对pog的爱,他决定只找编号连续的点,即l i  

~r i  

。

输入描述

若干组数据(不超过3 

组n≥10000 

或Q≥10000 

)。
每组数据第一行一个整数n(1≤n≤300000) 

,表示树的节点个数。
接下来n?1 

行,每行两个数A i ,B i  

,表示存在一条边连接这两个节点。
接下来一行一个数Q(1≤Q≤300000) 

,表示有Q 

组询问。
接下来Q行每行两个数l i ,r i (1≤li≤ri≤n) 

,表示询问编号为l i  

~r i  

的点的最近公共祖先。

输出描述

对于每组的每个询问,输出一行,表示编号为li~ri的点的最近公共祖先的编号。

输入样例

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

输出样例

1
1
3
3
1

思路:

做这题的方法有很多。下面给出2种解法。
1:维护一个跳表,表示编号为i ~i+2 j ?1 的LCA,注意在这里求LCA必须用O(1) 的做法才能通过所有数据。可以转换为RMQ,每次查询时只需查询两个数的LCA即可。
2:考虑dfs序,通过在简单的证明可知L~R的LCA为L ~R 中dfs序较小的那个位置与dfs序较大的那个位置的LCA。因此只要通过st表处理L~R最大dfs序与最小dfs序的编号即可。

方法一:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
const int N = 300000+1000;
int Q,n;
int head[N];
struct Edge
{
    int v,nxt;
}es[N<<1];
int cnt;

inline void add_edge(int u,int v)
{
    es[cnt].v=v;
    es[cnt].nxt=head[u];
    head[u]=cnt++;
    es[cnt].v=u;
    es[cnt].nxt=head[v];
    head[v]=cnt++;
}

int index;
int vs[N*2],id[N],dep[N];
int lca[N*2][20];
int minn[N][20];
int maxn[N][20];
void dfs(int u,int fa,int h)
{
    id[u]=++index;
    vs[index]=u;
    dep[u]=h;
    for(int i=head[u];~i;i=es[i].nxt)
    {
        int v=es[i].v;
        if(v==fa)continue;
        dfs(v,u,h+1);
        vs[++index]=u;
    }
}

int mm[2*N+100];
void ini()
{
    memset(head,-1,sizeof(head));
    cnt=index=0;
}
int main()
{
    mm[0]=-1;
    for(int i=1;i<=2*N;i++) mm[i]= (((i-1)&i)==0)? mm[i-1]+1:mm[i-1];
    while(~scanf("%d",&n))
    {
        ini();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        dfs(1,1,0);
        for(int i=1;i<=index;i++) lca[i][0]=vs[i];
        for(int j=1;j<=20;j++)
            for(int i=1;i+(1<<j)-1<=index;i++)
            {
                int a=lca[i][j-1],b=lca[i+(1<<(j-1))][j-1];
                lca[i][j] = dep[a]<dep[b] ? a:b;
            }
        for(int i=1;i<=n;i++) minn[i][0]=maxn[i][0]=id[i];
        for(int j=1;j<=20;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
            {
                minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
                maxn[i][j]=max(maxn[i][j-1],maxn[i+(1<<(j-1))][j-1]);
            }

        scanf("%d",&Q);
        for(int i=1;i<=Q;i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int k=mm[r-l+1];
            int L=min(minn[l][k],minn[r-(1<<k)+1][k]);
            int R=max(maxn[l][k],maxn[r-(1<<k)+1][k]);
            k=mm[R-L+1];
            int a=lca[L][k],b=lca[R-(1<<k)+1][k];
            int ans = dep[a]<dep[b]? a:b;
            printf("%d\n",ans);

        }
    }
    return 0;
}

方法二:(防止爆栈就换成bfs)
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int N = 300000+1000;
const int DEG  = 20;
int Q,n;
int pa[N][20];
int dep[N];
int head[N];
struct Edge
{
    int v,nxt;
}es[N<<1];
int cnt;
inline void add_edge(int u,int v)
{
    es[cnt].v=v;
    es[cnt].nxt=head[u];
    head[u]=cnt++;
    es[cnt].v=u;
    es[cnt].nxt=head[v];
    head[v]=cnt++;
}
/*
void bfs(int root)
{
    queue<int>q;
    dep[root]=0;
    pa[root][0]=root;
    q.push(root);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=1;i<DEG;i++)
            pa[u][i]=pa[pa[u][i-1]][i-1];
        for(int i=head[u];~i;i=es[i].nxt)
        {
            int v=es[i].v;
            if(v==pa[u][0]) continue;
            pa[v][0]=u;
            dep[v]=dep[u]+1;
            q.push(v);
        }
    }
}
*/
void dfs(int u,int fa,int h)
{
    dep[u]=h;
    pa[u][0]=fa;
    for(int i=1;i<DEG;i++) pa[u][i]=pa[pa[u][i-1]][i-1];
    for(int i=head[u];~i;i=es[i].nxt)
    {
        int v=es[i].v;
        if(v!=fa) dfs(v,u,h+1);
    }
}
int dp[N][20];
int LCA(int u,int v)
{
    if(dep[u]>dep[v]) swap(u,v);
    for(int det=dep[v]-dep[u],i=0;det;det>>=1,i++)
        if(det&1) v=pa[v][i];
    if(u==v) return u;
    for(int i=DEG-1;i>=0;i--)
        if(pa[u][i]!=pa[v][i]) v=pa[v][i],u=pa[u][i];
    return pa[u][0];
}
int mm[N];
void ini()
{
    memset(head,-1,sizeof(head));
    cnt=0;
}
int main()
{
    mm[0]=-1;
    for(int i=1;i<=N-1;i++)mm[i]= (((i-1)&i)==0)? mm[i-1]+1:mm[i-1];
    while(~scanf("%d",&n))
    {
        ini();
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }
        dfs(1,1,0);
        for(int i=1;i<=n;i++) dp[i][0]=i;
        for(int j=1;j<=20;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                dp[i][j]=LCA(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);

        scanf("%d",&Q);
        for(int i=1;i<=Q;i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            int k=mm[r-l+1];
            int ans=LCA(dp[l][k],dp[r-(1<<k)+1][k]);
            printf("%d\n",ans);
        }
    }
    return 0;

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-15 02:50:32

HDU 5266 pog loves szh III (LAC)的相关文章

hdu 5266 pog loves szh III 在线lca+线段树区间优化

题目链接:hdu 5266 pog loves szh III 思路:因为它查询的是区间上的lca,所以我们需要用在线lca来处理,达到单点查询的复杂度为O(1),所以我们在建立线段树区间查询的时候可以达到O(1*nlgn)的时间复杂度 ps:因为栈很容易爆,所以.....你懂的 -->#pragma comment(linker, "/STACK:1024000000,1024000000") /*****************************************

HDU 5266 pog loves szh III ( LCA + SegTree||RMQ )

pog loves szh III Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 470    Accepted Submission(s): 97 Problem Description Pog and Szh are playing games. Firstly Pog draw a tree on the paper. He

HDU 5266 pog loves szh III (线段树+在线LCA转RMQ)

题目地址:HDU 5266 这题用转RMQ求LCA的方法来做的非常简单,只需要找到l-r区间内的dfs序最大的和最小的就可以,那么用线段树或者RMQ维护一下区间最值就可以了.然后就是找dfs序最大的点和dfs序最小的点的最近公共祖先了. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #inc

HDU 5266 pog loves szh III (LCA)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5266 题目就是让你求LCA,模版题.注意dfs会栈溢出,所以要扩栈,或者用bfs写. 1 #pragma comment(linker, "/STACK:102400000,102400000") //扩栈 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 using nam

HDU 5266 pog loves szh III

题意:给出一棵树,1为根节点,求一段区间内所有点的最近公共祖先. 解法:用一棵线段树维护区间LCA.LCA是dp做法.dp[i][j]表示点i的第2^j个祖先是谁,转移方程为dp[i][j] = dp[dp[i][j - 1]][j - 1],初始的dp[i][0]可以用一次dfs求得,这样可以用logn的时间求第x个祖先或查询LCA.求第x个祖先可以从二进制的角度理解,假设x是10,转化为二进制是1010,那么只要升2^3 + 2^1个深度就可以求出第x个祖先.求LCA的具体做法是,先将点a和

hdu 2122 Ice_cream’s world III(最小生成树)

感觉就是 畅通工程的改版 直接贴代码了 #include<stdio.h> #include<string.h> #include<math.h> #include<iostream> #include<algorithm> #include<queue> #include<stack> #define mem(a,b) memset(a,b,sizeof(a)) #define ll __int64 #define MA

hdu 5265 pog loves szh II STL

pog loves szh II Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5265 Description pog在与szh玩游戏,首先pog找到了一个包含n个数的序列,然后他在这n个数中挑出了一个数A,szh出于对pog的爱,在余下的n−1个数中也挑了一个数B,那么szh与pog的恩爱值为(A+B)对p取模后的余数,pog与szh当然想让恩爱值越高越好,并且他们

hdu 5264 pog loves szh I 水题

pog loves szh I Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5264 Description pog拥有很多字符串,它喜欢将两个长度相等字符串交错拼在一起,如abcd与efgh,那么交错拼在一起就成了aebfcgdh啦!szh觉得这并不好玩,因此它将第二个字符串翻转了一遍,如efgh变成了hgfe,然后再将这两个字符串交错拼在一起,因此abcd与efg

hdu 5264 pog loves szh I

题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5264 pog loves szh I Description Pog has lots of strings. And he always mixes two equal-length strings. For example, there are two strings: "abcd" and "efgh". After mixing, a new string &q