NOIP2013D1T3-货车运输

问题描述

A 国有 n 座城市,编号从 1 到 n ,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。、
输入格式
第一行有两个用一个空格隔开的整数 n,m ,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x,y,z ,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y ,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式
共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出 -1 。
样例输入

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

样例输出

3
-1
3

思路分析

首先便是想到了Floyd的暴力方法,状态转移方程也不难推出:w[i][j]=min(w[i][j], w[i][k]+w[k][j]);但是 n^3次方时间复杂度和 n^2 的空间复杂度是显然不可取的。
于是我们思考,可以发现有一些权值较小的边是不会被走过的。正如样例中的第三条边,就算有其他的很多条边,这条边无论如何也是不会被走过的。于是我们想到了可以将图中这样的边去掉,按照这个思路我们便想到了构造最大生成树,将其余的边去除。
得到了这样一个树之后,我们便考虑如何求出两个节点之间最小边权的最大值(即为题中的最大载重),因为这两点之间的路径是唯一的,我们只需要找出这条路径便可以得到答案。我们可以通过LCA来做到这一点,我求LCA的方法是先从每一个根节点进行搜索,求出节点深度等信息,然后利用这些信息进行树上倍增。
于是我们可以得出大体思路:首先重新建图,构造出最大生成树,然后在最大生成树上求LCA来回答询问。

#include <cstdio>
int w[100001][21],p[100001][21],head[1000001],deep[1000001],fa[1000001];
struct nodea{ int x,y,c; } t[1000001];
struct nodeb{ int x,y,c,g; } h[1000001];
int inf=999999999,len=0,n=0,m=0,q=0;
void px(int l,int r)
{
    int x=l,y=r,mid=t[(l+r)/2].c;
    while(x<=y)
    {
        while(t[x].c>mid)
        {
            x++;
        }
        while(t[y].c<mid)
        {
            y--;
        }
        if(x<=y)
        {
            nodea pt=t[x];
            t[x]=t[y];
            t[y]=pt;
            x++;
            y--;
        }
    }
    if(l<y)
    {
        px(l,y);
    }
    if(x<r)
    {
        px(x,r);
    }
}
int min(int x,int y)
{
    return x<y?x:y;
}
int init()
{
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
        head[i]=0;
        deep[i]=0;
    }
}
void ins(int x,int y,int c)
{
    len++;
    h[len].x=x;
    h[len].y=y;
    h[len].c=c;
    h[len].g=head[x];
    head[x]=len;
}
int find(int t)
{
    if(fa[t]==t)
    {
        return t;
    }
    else
    {
        return fa[t]=find(fa[t]);
    }
}
void kruskal()
{
    int p=0;
    for(int i=1;i<=m;i++)
    {
        int tx=find(t[i].x);
        int ty=find(t[i].y);
        if(tx!=ty)
        {
            fa[tx]=ty;
            ins(t[i].x,t[i].y,t[i].c);
            ins(t[i].y,t[i].x,t[i].c);
            p++;
            if(p==n-1)
            {
                break;
            }
        }
    }
}
void dfs(int u)
{
    for(int i=head[u];i>0;i=h[i].g)
    {
        int y=h[i].y;
        if(deep[y]==0)
        {
            deep[y]=deep[u]+1;
            p[y][0]=u;
            w[y][0]=h[i].c;
            dfs(y);
        }
    }
}
void ycl()
{
    for(int i=1;i<=n;i++)
    {
        if(deep[i]==0)
        {
            deep[i]=1;
            p[i][0]=0;
            dfs(i);
        }    
    }
    dfs(1);
    for(int i=1;i<=20;i++)
    {
        for(int x=1;x<=n;x++)
        {
            p[x][i]=p[p[x][i-1]][i-1];
            w[x][i]=min(w[x][i-1],w[p[x][i-1]][i-1]);
        }
    }
}
int lca(int x,int y)
{
    int ans=inf;
    if(deep[x]<deep[y])
    {
        int t=x;
        x=y;
        y=t;
    }
    for(int i=20;i>=0;i--)
    {
        if(deep[p[x][i]]>=deep[y])
        {
            ans=min(ans,w[x][i]);
            x=p[x][i];
        }
    }
    if(x==y)
    {
        return ans;
    }
    for(int i=20;i>=0;i--)
    {
        if(p[x][i]!=p[y][i])
        {
            ans=min(ans,min(w[x][i],w[y][i]));
            x=p[x][i];
            y=p[y][i];
        }
    }
    ans=min(ans,min(w[x][0],w[y][0]));
    return ans;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d %d %d",&t[i].x,&t[i].y,&t[i].c);
    }
    px(1,m);
    init();
    kruskal();
    ycl();
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        int dx=0,dy=0;
        scanf("%d %d",&dx,&dy);
        if(find(dx)!=find(dy))
        {
            printf("-1\n");
        }
        else
        {
            printf("%d\n",lca(dx,dy));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Liukairui/article/details/81285818