Kruskal,最小生成树,树链剖分,LCA(邦德,UVA 11354)

又是最小生成树+树链剖分的经典组合题。

还有一种做法,就是大白书上的LCA做法。


树链剖分就将线段树应用在树上。

LCA有点把ST表应用在树上。只是有点像而已。毕竟LCA是O(logn)回答,而ST表是O(1)回答。


写LCA时因为不够理解算法所以有个地方写错了WA了好久。


代码

#include<bits/stdc++.h>
#define ls (now<<1)
#define rs (ls|1)
using namespace std;
const int maxn = 50010;
const int maxm = 100010;

int n,m;
vector<int>G[maxn];
vector<int>W[maxn];
int u[maxm],v[maxm],w[maxm];
int r[maxm];
int cmp(int i,int j){return w[i]<w[j];}
int fa[maxn];
int f(int x){return fa[x]==x?x:fa[x]=f(fa[x]);}
void Kruskal()
{
    for(int i=0;i<n;i++) fa[i]=i,G[i].clear(),W[i].clear();
    for(int i=0;i<m;i++) r[i]=i;
    sort(r,r+m,cmp);
    for(int i=0;i<m;i++)
    {
        int e=r[i];
        int x=f(u[e]);
        int y=f(v[e]);
        if(x!=y)
        {
            fa[x]=y;
            G[u[e]].push_back(v[e]);
            G[v[e]].push_back(u[e]);
            W[u[e]].push_back(w[e]);
            W[v[e]].push_back(w[e]);
        }
    }
}

int tree[maxn<<2];
void add(int l,int r,int now,int pos,int val)
{
    if(l==r)
    {
        tree[now]=val;
        return;
    }
    int m=l+(r-l)/2;
    if(pos<=m) add(l,m,ls,pos,val);
    else add(m+1,r,rs,pos,val);
    tree[now]=max(tree[ls],tree[rs]);
}

int siz[maxn],fat[maxn],son[maxn],dep[maxn];

void dfs1(int u,int f,int d)
{
    siz[u]=1;
    fat[u]=f;
    son[u]=-1;
    dep[u]=d;
    for(unsigned int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==f) continue;
        dfs1(v,u,d+1);
        siz[u]+=siz[v];
        if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
    }
}

int tim,tid[maxn],top[maxn],rnk[maxn];
void dfs2(int u,int tp)
{
    tid[u]=++tim;
    rnk[tid[u]]=u;
    top[u]=tp;
    if(son[u]==-1) return;
    dfs2(son[u],tp);
    for(unsigned int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(v==fat[u]) continue;
        if(v==son[u])
        {
            add(1,n,1,tid[v],W[u][i]);
            continue;
        }
        dfs2(v,v);
        add(1,n,1,tid[v],W[u][i]);
    }
}

int qry(int l,int r,int now,int ql,int qr)
{
    if(ql<=l&&r<=qr) return tree[now];
    if(ql>r||qr<l) return 0;
    int m=l+(r-l)/2;
    return max(qry(l,m,ls,ql,qr),qry(m+1,r,rs,ql,qr));
}

int RMQ(int x,int y)
{
    int tpx=top[x],tpy=top[y];
    int ret=0;
    while(x!=y)
    {
        if(tpx!=tpy)
        {
            if(dep[tpx]<dep[tpy])
            {
                swap(x,y);
                swap(tpx,tpy);
            }
            ret=max(ret,qry(1,n,1,tid[tpx],tid[x]));
            x=fat[tpx];tpx=top[x];
        }
        else
        {
            if(dep[x]<dep[y])
            {
                swap(x,y);
                swap(tpx,tpy);
            }
            ret=max(ret,qry(1,n,1,tid[y]+1,tid[x]));
            return ret;
        }
    }
    return ret;
}

int flag;

int main()
{
    while(scanf("%d %d",&n,&m)==2)
    {
        if(flag) puts("");
        flag=1;
        for(int i=0;i<m;i++)
            scanf("%d %d %d",u+i,v+i,w+i),
            u[i]--,v[i]--;
        Kruskal();
        add(1,n,1,1,0);
        dfs1(0,-1,0);
        tim=0;
        dfs2(0,0);
        int q,s,t;
        scanf("%d",&q);
        while(q--)
        {
            scanf("%d %d",&s,&t);
            printf("%d\n",RMQ(s-1,t-1));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值