NOIP 2013 P1967 货车运输

倍增求LCA+最大生成树

题目给出的是一张图,在图上有很多算法无法实现,所以要将其转化为树

题中可以发现货车的最后的载重量是由权值最小的一条边决定的,所以我们求最大生成树

求完最大生成树后我们得到一个森林

现在转化为了求两点路径经过边的边权的最小值,用倍增算法进行计算

#include <bits/stdc++.h>
#define inf 1e9
using namespace std;
const int MAXN=10100;
int n,m,first[MAXN],nxt[MAXN*10],point[MAXN*10],len[MAXN*10];
int q,tot,fa[MAXN],f[MAXN][21],chmin[MAXN][21],vi[MAXN],de[MAXN];
struct node
{
    int u,v,len;
}sh[MAXN*5];
void add_edge(int x,int y,int z)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    point[tot]=y;
    len[tot]=z;
}
void dfs(int x,int father,int deep,int last,int visit)
{
    de[x]=deep;
    vi[x]=visit;
    f[x][0]=father;//倍增father数组
    chmin[x][0]=last;//记录上一条边的权值
    for (int i=first[x];i!=-1;i=nxt[i])
    {
        if (point[i]!=father)
        {
            dfs(point[i],x,deep+1,len[i],visit);
        }
    }
}
int getfather(int x)
{
    if (fa[x]==x)
      return fa[x];
    fa[x]=getfather(fa[x]);
    return fa[x];
}
bool cmp(node a,node b)
{
    return a.len>b.len;
}
int lca(int a,int b)//倍增求LCA
{
    if (getfather(a)!=getfather(b))
      return -1;
    if (de[a]>de[b])
      swap(a,b);
    int MIN=inf;
    for (int i=20;i>=0;i--)
    {
        if (de[f[b][i]]>=de[a])
        {
            MIN=min(MIN,chmin[b][i]);
            b=f[b][i];
        }
    }
    if (a==b)
      return MIN;
    for (int i=20;i>=0;i--)
    {
        if (f[a][i]!=f[b][i])
        {
            MIN=min(MIN,min(chmin[a][i],chmin[b][i]));
            a=f[a][i];
            b=f[b][i];
        }
    }
    MIN=min(MIN,min(chmin[a][0],chmin[b][0]));
    return MIN;
}
int main()
{
    tot=-1;
    memset(nxt,-1,sizeof(nxt));
    memset(first,-1,sizeof(first));
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&sh[i].u,&sh[i].v,&sh[i].len);
    }
    for (int i=1;i<=n;i++)
      fa[i]=i;
    sort(sh+1,sh+1+m,cmp);
    for (int i=1;i<=m;i++)
    {
        if (getfather(sh[i].u)!=getfather(sh[i].v))
        {
            fa[getfather(sh[i].u)]=getfather(sh[i].v);
            add_edge(sh[i].u,sh[i].v,sh[i].len);
            add_edge(sh[i].v,sh[i].u,sh[i].len);
        }
    }//求最大生成树
    int now=0;
    for (int i=1;i<=n;i++)//可能有多棵树,每棵树分别考虑
    {
        if (vi[i]!=0)
          continue;
        now++;
        dfs(i,0,0,inf,now);
        f[i][0]=i;
        chmin[i][0]=inf;
    }
    for (int j=1;j<=20;j++)
    {
        for (int i=1;i<=n;i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
            chmin[i][j]=min(chmin[i][j-1],chmin[f[i][j-1]][j-1]);//进行倍增
        }
    }
    scanf("%d",&q);
    for (int i=1;i<=q;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
}

 

倍增求LCA时避免父亲倍增数组出现0,倍增记录最小值时,简单写法

    int now=0;
    for (int i=1;i<=n;i++)
    {
        if (vi[i]!=0)
          continue;
        now++;
        dfs(i,0,0,inf,now);
        f[i][0]=i;//使当前的father==i
        chmin[i][0]=inf;//记录的最小值
    }

求deep时,注意不是求距离上的deep,而是层数

void dfs(int x,int father,int deep,int last,int visit)
{
    de[x]=deep;
    vi[x]=visit;
    f[x][0]=father;
    chmin[x][0]=last;
    for (int i=first[x];i!=-1;i=nxt[i])
    {
        if (point[i]!=father)
        {
            dfs(point[i],x,deep+1,len[i],visit);//deep是+1,不是加len[i],注意
        }
    }
}

 

posted @ 2019-07-03 20:28  SevenDawns  阅读(129)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end