RMQ转换LCA模板 ST算法

原理:

void dfs(int k,int d,long long sum)
{
    cost[k]=sum;
    pos[k]=++tot; ///记录第一次出现的时间戳
    F[tot]=k; ///记录欧拉序列
    rmq[tot]=d;///记录该时间戳深度
    vis[k]=1;
    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(!vis[e])
        {
            dfs(e,d+1,sum+w[i]);
            F[++tot]=k; ///继续记录访问时间戳,那么此时就相当于在每个子节点中插入了父节点,
                        ///在利用pos下标查询RMQ时父节点的信息就卡在了两者之间
            rmq[tot]=d;
        }
    }
}

ST(来源 :kuangbin模板):

int F[maxn],pos[maxn],tot;
long long cost[maxn];

int rmq[maxn];//rmq数组,就是欧拉序列对应的深度序列
struct ST
{
    int mm[2*maxn];
    int dp[2*maxn][20];//最小值对应的下标
    void init(int n)
    {
        mm[0] = -1;
        for(int i = 1; i <= n; i++)
        {
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
            dp[i][0] = i;
        }
        for(int j = 1; j <= mm[n]; j++)
            for(int i = 1; i + (1<<j) - 1 <= n; i++)
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)//查询[a,b]之间最小值的下标
    {
        if(a > b)swap(a,b);
        int k = mm[b-a+1];
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
} st;

初始化:

void LCA_init()
{
    memset(vis,0,sizeof vis);
    for(int i=1; i<=n; i++)
    {
        if(!vis[i])
        {
            dfs(i,1,0);
        }
    }
    st.init(tot);
}


查询部分:

long long Query(int s,int t)
{
    int lca=F[ st.query(pos[s],pos[t]) ];
    return cost[s]+cost[t]-2*cost[lca];
}

HDU 2586  How far away ?


#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 80004

int fir[maxn],nex[maxn],v[maxn],w[maxn],e_max;
int pos[2*maxn],T[2*maxn],tot;
long long cost[maxn];
int rmq[2*maxn];//rmq数组,就是欧拉序列对应的深度序列
struct ST
{
    int mm[2*maxn];
    int dp[2*maxn][20];//最小值对应的下标
    void init(int n)
    {
        mm[0] = -1;
        for(int i = 1; i <= n; i++)
        {
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
            dp[i][0] = i;
        }
        for(int j = 1; j <= mm[n]; j++)
            for(int i = 1; i + (1<<j) - 1 <= n; i++)
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)//查询[a,b]之间最小值的下标
    {
        if(a > b)swap(a,b);
        int k = mm[b-a+1];
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
}LCA;

void init()
{
    memset(fir,-1,sizeof fir);
    e_max=0;
    tot=1;
}

void add_edge(int s,int t,int c)
{
    int e=e_max++;
    v[e]=t;
    w[e]=c;
    nex[e]=fir[s];
    fir[s]=e;
}

void dfs(int k,int pre,int d,long long sum)
{
    cost[k]=sum;
    pos[k]=tot;
    T[tot]=k;
    rmq[tot++]=d;
    for(int i=fir[k]; ~i; i=nex[i])
    {
        int e=v[i];
        if(e==pre) continue;
        dfs(e,k,d+1,sum+w[i]);
        T[tot]=k;
        rmq[tot++]=d;
    }
}

void LCA_init()
{
    dfs(1,-1,1,0);
    LCA.init(tot-1);
}

long long Query(int s,int t)
{
    int lca=T[LCA.query(pos[s],pos[t])];
    return cost[s]+cost[t]-2*cost[lca];
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        init();
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<n; i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add_edge(a,b,c);
            add_edge(b,a,c);
        }
        LCA_init();
        while(m--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            long long ans=Query(l,r);
            printf("%lld\n",ans);
        }
    }
    return 0;
}

POJ 1986 Distance Queries

#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 80004

int x[maxn],y[maxn],c[maxn],e[maxn],s[maxn],pos[2*maxn];
int fir[maxn],nex[maxn],v[maxn],w[maxn],e_max,tot;
long long cost[maxn];

int rmq[2*maxn],F[2*maxn];//rmq数组,就是欧拉序列对应的深度序列
struct ST
{
    int mm[2*maxn];
    int dp[2*maxn][20];//最小值对应的下标
    void init(int n)
    {
        mm[0] = -1;
        for(int i = 1; i <= n; i++)
        {
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
            dp[i][0] = i;
        }
        for(int j = 1; j <= mm[n]; j++)
            for(int i = 1; i + (1<<j) - 1 <= n; i++)
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)//查询[a,b]之间最小值的下标
    {
        if(a > b) swap(a,b);
        int k = mm[b-a+1];
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
} LCA;

void init()
{
    memset(fir,-1,sizeof fir);
    e_max=0;
    tot=1;
}

void add_edge(int s,int t,int c)
{
    int e=e_max++;
    v[e]=t;
    w[e]=c;
    nex[e]=fir[s];
    fir[s]=e;
}

void dfs(int k,int pre,int d,long long sum)
{
    pos[k]=tot;
    rmq[tot]=d;
    F[tot++]=k;
    cost[k]=sum;
    for(int i=fir[k]; ~i; i=nex[i])
    {
        int e=v[i];
        if(e!=pre)
        {
            dfs(e,k,d+1,sum+w[i]);
            rmq[tot]=d;
            F[tot++]=k;
        }
    }
}

void LCA_init()
{
    dfs(1,-1,1,0);
    LCA.init(tot-1);
}

long long Query(int s,int t)
{
    int lca=F[LCA.query(pos[s],pos[t])];
    return cost[s]+cost[t]-2*cost[lca];
}

bool cmp(int a,int b)
{
    return c[a]<c[b];
}

int root(int x)
{
    return s[x]==x?x:s[x]=root(s[x]);
}

int n,m;
void build()
{
    for(int i=0; i<=n; i++) s[i]=i;
    sort(e+1,e+m+1,cmp);
    for(int i=1; i<=m; i++)
    {
        int r=e[i];
        int _x=root(x[r]);
        int _y=root(y[r]);
        if(_x!=_y)
        {
            s[_x]=_y;
            add_edge(x[r],y[r],c[r]);
            add_edge(y[r],x[r],c[r]);
        }
    }
}

int main()
{

    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1; i<=m; i++)
        {
            char str[5];
            scanf("%d%d%d%s",&x[i],&y[i],&c[i],str);
            e[i]=i;
        }
        build();
        LCA_init();
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%lld\n",Query(l,r));
        }
    }
    return 0;
}


HDU 2874 Connections between cities


#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 40004

int x[maxn],y[maxn],c[maxn],e[maxn];
int vis[maxn];
int fir[maxn],nex[maxn],v[maxn],w[maxn],e_max,s[maxn];
int F[maxn],pos[maxn],tot;
long long cost[maxn];

int rmq[maxn];//rmq数组,就是欧拉序列对应的深度序列
struct ST
{
    int mm[2*maxn];
    int dp[2*maxn][20];//最小值对应的下标
    void init(int n)
    {
        mm[0] = -1;
        for(int i = 1; i <= n; i++)
        {
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
            dp[i][0] = i;
        }
        for(int j = 1; j <= mm[n]; j++)
            for(int i = 1; i + (1<<j) - 1 <= n; i++)
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)//查询[a,b]之间最小值的下标
    {
        if(a > b)swap(a,b);
        int k = mm[b-a+1];
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
} st;

void init()
{
    e_max=0;
    tot=0;
    memset(fir,-1,sizeof fir);
}

void add_edge(int s,int t,int c)
{
    int e=e_max++;
    v[e]=t;
    w[e]=c;
    nex[e]=fir[s];
    fir[s]=e;
}

bool cmp(int a,int b)
{
    return c[a]<c[b];
}

int root(int x)
{
    return s[x]==x?x:s[x]=root(s[x]);
}

int n,m,q;
void build()
{
    sort(e,e+m,cmp);
    for(int i=0; i<=n; i++) s[i]=i;
    for(int i=0; i<m; i++)
    {
        int r=e[i];
        int _x=root(x[r]);
        int _y=root(y[r]);
        if(_x!=_y)
        {
            s[_x]=_y;
            add_edge(x[r],y[r],c[r]);
            add_edge(y[r],x[r],c[r]);
        }
    }
}

void dfs(int k,int d,long long sum)
{
    cost[k]=sum;
    pos[k]=++tot;
    F[tot]=k;
    rmq[tot]=d;
    vis[k]=1;
    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(!vis[e])
        {
            dfs(e,d+1,sum+w[i]);
            F[++tot]=k;
            rmq[tot]=d;
        }
    }
}

void LCA_init()
{
    memset(vis,0,sizeof vis);
    for(int i=1; i<=n; i++)
    {
        if(!vis[i])
        {
            dfs(i,1,0);
        }
    }
    st.init(tot);
}

long long Query(int s,int t)
{
    int lca=F[ st.query(pos[s],pos[t]) ];
    return cost[s]+cost[t]-2*cost[lca];
}
int main()
{

    while(scanf("%d%d%d",&n,&m,&q)!=EOF)
    {
        init();
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&x[i],&y[i],&c[i]);
            e[i]=i;
        }
        build();
        LCA_init();
        while(q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            if(root(l)!=root(r))
                printf("Not connected\n");
            else  printf("%lld\n",Query(l,r));
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值