倍增及其应用

本文介绍了如何利用倍增思想优化在线性递推中的复杂度,并通过ST表(区间最值问题解决方案)预处理和查询方法。同时,详细讲解了如何运用倍增法求解树上最近公共祖先(LCA)。实例展示了在P3379模板题中的应用。
摘要由CSDN通过智能技术生成

一、倍增思想

·在线性递推复杂度过大的时候,引入倍增思想,只递推状态空间中2的整数次幂位置的状态。
·任何整数都可以表示为若干个2的整数次幂的和。

二、ST表

ST表是用于解决可重复贡献问题的数据结构。
(如区间最值问题)
1、预处理
预处理lg数组,记录 l o g 2 N log_2N log2N的整数值,避免浮点误差

void init(){
    lg[0]=0;
    for(int i=2;i<=n;i++){
        lg[i]=lg[i-1]+(1<<(lg[i-1]+1)==i);
    }
}

预处理st数组,规定st[i][j]是以i为开头,长度为 2 j 2^j 2j的序列内最大值。

for(int i=1;i<=n;i++){
        st[i][0]=a[i];
    }
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            st[i][j]=max(st[i][j-1],st[i+(1<<j)][j-1]);
        }
    }

2、查询
和更新st表思想相同,找到包含[l,r]区间内所有元素的两段st表值,再取max即可。

int search(int l,int r){
    int k=lg[r-l+1];
    int t=max(st[l][k],st[r-(1<<k)+1][k]);
    return t;
}

三、LCA

lca指树上最近公共祖先,求lca可以利用倍增的思想。
1、预处理
dfs这棵树,得到每个节点到根的深度以及预处理倍增祖先。

void dfs(int x,int pre){
    dep[x]=dep[pre]+1;
    f[x][0]=pre;
    for(int i=1;(1<<i)<=N;i++){
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for(int i=0;i<mp[x].size();i++){
        if(mp[x][i]!=pre)dfs(mp[x][i],x);
    }
}

2、lca查询过程

int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    int dx=dep[x]-dep[y];
    for(int i=0;(1<<i)<=dx;i++){
        if((1<<i)&dx)x=f[x][i];
    }
    if(x==y)return x;
    if(x!=y){
        //一起向上跳
        for(int i=log2(N);i>=0;i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i]; y=f[y][i];//当前祖先不相等,更新x,y
            }
        }
    }
    return f[y][0];
}

模版题:P3379 【模板】最近公共祖先(LCA)

//倍增法求lca
const int maxn=5e5+7;
vector<int> mp[maxn];
int dep[maxn];//x节点相对父节点的深度
int f[maxn][30];//f[x][i]=x向上走2^i个节点
int N,M,S;
void dfs(int x,int pre){
    dep[x]=dep[pre]+1;
    f[x][0]=pre;
    for(int i=1;(1<<i)<=N;i++){
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for(int i=0;i<mp[x].size();i++){
        if(mp[x][i]!=pre)dfs(mp[x][i],x);
    }
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    int dx=dep[x]-dep[y];
    //cout<<"dx="<<dx<<endl;
    for(int i=0;(1<<i)<=dx;i++){
        if((1<<i)&dx)x=f[x][i];
    }
    if(x==y)return x;
    if(x!=y){
        //一起向上跳
        for(int i=log2(N);i>=0;i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i]; y=f[y][i];//当前祖先不相等,更新x,y
            }
        }
    }
    return f[y][0];
}
int main(){
    read(N);read(M);read(S);
    for(int i=1;i<=N-1;i++){
        int a,b;
        read(a);read(b);
        mp[a].pb(b);
        mp[b].pb(a);
    }
    dfs(S,0);
    while(M--){
        int x,y;
        read(x);read(y);
        cout<<lca(x,y)<<endl;
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KaaaterinaX

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值