【洛谷P1967】货车运输

这个题要求货车从a到b最大能运多少货物(不能输出-1),那么自然而然的就可以想到最大生成树,这个很好求,重点在于如何快速的查找树上两点间的最大边权,这个时候我们可以运用倍增来解决,因为这两个点都在树上,显然联通它们的路径上有些边是一定要走的,这些边就是它们到最近公共祖先的边,那么答案就在这些边当中,那么我们可以运用lca的办法,设mx[i][j]表示i到它的2^j号父亲的路上边权的最小值是多少

这个题难点就在于细节上,要注意提前把所有点的深度初始化为1

#include<algorithm>
#include<iostream> 
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
int n,m,q,a,b,tail,tot,head[10010],mx[10010][20],fa[10010],f[10010][20],de[10010];
bool flag[10010]; 
struct in
{
    int x,y,z;
}ter[50050];
struct es
{
    int to,ne,co;
}ing[20020];
in ex[10010];
bool cmp(in a,in b)
{
    return a.z>b.z;
}
int find(int x)
{
    if(fa[x]==x)
        return x;
    return fa[x]=find(fa[x]);
}
inline void kru()
{
    for(int i=1;i<=n;i++)
        fa[i]=i;//初始化所有的点的父亲,均为它们自己 
    tail=1,tot=0;
    while(tail<=m&&tot<n-1)
    {
        int owo=find(ter[tail].x),wow=find(ter[tail].y);
        if(owo!=wow)//合并 
        {
            fa[owo]=wow;
            ex[++tot]=ter[tail];
        }
        tail++;
    }
}
inline void build(int f,int l,int c)//双向建图 
{
    ing[++tail]=(es){l,head[f],c},head[f]=tail;
    ing[++tail]=(es){f,head[l],c},head[l]=tail;
}
void dfs(int x)//提前预处理深度,每个点2^0的父亲及其答案 
{
    flag[x]=1;
    for(int i=head[x];i!=-1;i=ing[i].ne)
    {
        int t=ing[i].to;
        if(flag[t])
            continue;
        f[t][0]=x,mx[t][0]=ing[i].co,de[t]=de[x]+1;
        dfs(t); 
    }
}
int lca(int a,int b)
{
    if(de[a]<de[b])//保证a在下面,便于求lca 
        swap(a,b);
    for(int i=log2(n);i>=0;i--)
    {
        if(de[f[a][i]]>=de[b])
            a=f[a][i];
    }
    if(a==b)//如果a b在a经过跳转后都在同一个点上,说明答案为a 
        return a;
    for(int i=log2(n);i>=0;i--)
        if(f[a][i]!=f[b][i])
            a=f[a][i],b=f[b][i];
    return f[a][0];//不停跳,直到两点上方为最近公共祖先 
}
int ask(int a,int b)
{
    int re=1000000007;
    for(int i=log2(n);i>=0;i--)//模仿求lca
        if(de[f[a][i]]>=de[b])
            re=min(re,mx[a][i]),a=f[a][i];
    return re;
}
inline void make_lca()//提前预处理好i的2^j的父亲及其答案 
{
    for(int i=1;i<=log2(n);i++)
        for(int j=1;j<=n;j++)
            f[j][i]=f[f[j][i-1]][i-1],mx[j][i]=min(mx[f[j][i-1]][i-1],mx[j][i-1]);
} 
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&ter[i].x,&ter[i].y,&ter[i].z);
    sort(ter+1,ter+1+m,cmp);
    kru();//先求出能够构建最大生成树(森林)的边 
    memset(head,-1,sizeof(head)),tail=0;
    memset(de,1,sizeof(de));//防止它跑到0这个不存在的点上 
    de[0]=0;
    for(int i=1;i<=n-1;i++)
        build(ex[i].x,ex[i].y,ex[i].z);//建图 
    for(int i=1;i<=n;i++)
        if(!flag[i])//这样做是因为不排除森林的可能性 
            dfs(i);
    make_lca();
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&a,&b);
        if(find(a)!=find(b))//如果二者不在一棵树上,直接不可能到达 
        {
            printf("-1\n");continue;
        }
        else
        {
            int t=lca(a,b);//先求最近公共祖先 
            printf("%d\n",min(ask(a,t),ask(b,t)));//输出各自到公共祖先的路上的边的最小值 
        }
    }
}

 

#include<algorithm>#include<iostream> #include<cstring>#include<cstdio>#include<cmath>using namespace std;int n,m,q,a,b,tail,tot,head[10010],mx[10010][20],fa[10010],f[10010][20],de[10010];bool flag[10010]; struct in{int x,y,z;}ter[50050];struct es{int to,ne,co;}ing[20020];in ex[10010];bool cmp(in a,in b){return a.z>b.z;}int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);}inline void kru(){for(int i=1;i<=n;i++)fa[i]=i;//初始化所有的点的父亲,均为它们自己 tail=1,tot=0;while(tail<=m&&tot<n-1){int owo=find(ter[tail].x),wow=find(ter[tail].y);if(owo!=wow)//合并 {fa[owo]=wow;ex[++tot]=ter[tail];}tail++;}}inline void build(int f,int l,int c)//双向建图 {ing[++tail]=(es){l,head[f],c},head[f]=tail;ing[++tail]=(es){f,head[l],c},head[l]=tail;}void dfs(int x)//提前预处理深度,每个点2^0的父亲及其答案 {flag[x]=1;for(int i=head[x];i!=-1;i=ing[i].ne){int t=ing[i].to;if(flag[t])continue;f[t][0]=x,mx[t][0]=ing[i].co,de[t]=de[x]+1;dfs(t); }}int lca(int a,int b){if(de[a]<de[b])//保证a在下面,便于求lca swap(a,b);for(int i=log2(n);i>=0;i--){if(de[f[a][i]]>=de[b])a=f[a][i];}if(a==b)//如果a b在a经过跳转后都在同一个点上,说明答案为a return a;for(int i=log2(n);i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];return f[a][0];//不停跳,直到两点上方为最近公共祖先 }int ask(int a,int b){int re=1000000007;for(int i=log2(n);i>=0;i--)//模仿求lcaif(de[f[a][i]]>=de[b])re=min(re,mx[a][i]),a=f[a][i];return re;}inline void make_lca()//提前预处理好i的2^j的父亲及其答案 {for(int i=1;i<=log2(n);i++)for(int j=1;j<=n;j++)f[j][i]=f[f[j][i-1]][i-1],mx[j][i]=min(mx[f[j][i-1]][i-1],mx[j][i-1]);} int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++)scanf("%d%d%d",&ter[i].x,&ter[i].y,&ter[i].z);sort(ter+1,ter+1+m,cmp);kru();//先求出能够构建最大生成树(森林)的边 memset(head,-1,sizeof(head)),tail=0;memset(de,1,sizeof(de));//防止它跑到0这个不存在的点上 de[0]=0;for(int i=1;i<=n-1;i++)build(ex[i].x,ex[i].y,ex[i].z);//建图 for(int i=1;i<=n;i++)if(!flag[i])//这样做是因为不排除森林的可能性 dfs(i);make_lca();scanf("%d",&q);for(int i=1;i<=q;i++){scanf("%d%d",&a,&b);if(find(a)!=find(b))//如果二者不在一棵树上,直接不可能到达 {printf("-1\n");continue;}else{int t=lca(a,b);//先求最近公共祖先 printf("%d\n",min(ask(a,t),ask(b,t)));//输出各自到公共祖先的路上的边的最小值 }}}

posted @ 2017-10-29 14:48  那一抹落日的橙  阅读(276)  评论(1编辑  收藏  举报