[p1967] 货车运输

题目


这道题是贪心加倍增

首先要贪心的找出一棵最大生成树(题目中要求的是每个点到替他点的最优路径上,每条边的最小值,而不是最短路。建立一棵树,这样就能使所有点都联通。)

在树上做lca。

因为两点之间的边最大权值=两个点分别到最近公共祖先的边的权值的最大值

这样的话。就可以使用倍增

使用两个倍增数组

一个数组为为第i个点跳2^j步后到达的节点

另一个为第i个点跳2^j步后到达的节点与第i个点之间的最小权值

在线查询

#include<iostream> 
#include<cstdio>
#include<algorithm>
using namespace std;
struct ll
{
    int point1;
    int point2;
    int weight;
};
ll l[50100];//克鲁斯卡尔的数组
struct node
{
    int point;
    int next;
    int value;
};
node line[101000];//邻接表
int head[10100],tail;
void add(int x,int y,int val)//添加边
{
    line[++tail].point=y;
    line[tail].value=val;
    line[tail].next=head[x];
    head[x]=tail;
}
bool compare(const ll &a,const ll &b)
{
    return a.weight>b.weight;
}
int color[10100];//染色,判断是不是一个树中。然而luogu数据太弱辣!!没有这个也行
int t[10100][20];//tree的倍增
int minn[10100][20];//
int dep[10100];
void dfs(int now,int fa,int col,int dis)//预处理
{
    dep[now]=dep[fa]+1;
    t[now][0]=fa;
    minn[now][0]=dis;
    color[now]=col;
    for(int i=1;(1<<i)<dep[now];i++)
    {
        t[now][i]=t[t[now][i-1]][i-1];
        minn[now][i]=min(minn[now][i-1],minn[t[now][i-1]][i-1]);
    }
    int need=head[now];
    while(need!=-1)
    {
        if(line[need].point!=fa)
            dfs(line[need].point,now,col,line[need].value);
        need=line[need].next;
    }
    return ;
}
void lca(int x,int y)//lca
{
    int ans=0x7fffffff;
    if(dep[x]<dep[y])
        swap(x,y);
    for(int i=16;i>=0;i--)
        if(dep[t[x][i]]>=dep[y])
        {
            ans=min(ans,minn[x][i]);
            x=t[x][i];
        }
    if(x!=y)//判断是不是一个点
    {
        
        for(int i=16;i>=0;i--)
        {
            if(t[x][i]!=t[y][i])
            {
                ans=min(ans,min(minn[x][i],minn[y][i]));
                x=t[x][i];
                y=t[y][i];
            }
        }
        ans=min(ans,min(minn[x][0],minn[y][0]));
    }
    printf("%d
",ans);
    return ;
}
int f[10100];
int find(int x)
{
    if(f[x]==x)
        return x;
    return f[x]=find(f[x]);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        head[i]=-1;
        f[i]=i;
    }
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&l[i].point1,&l[i].point2,&l[i].weight);
    sort(l+1,l+1+m,compare);
    int i=1;
    while(i<=m)
    {
        int f1=find(l[i].point1),f2=find(l[i].point2);
        if(f1!=f2)
        {
            f[f1]=f2;
            add(l[i].point1,l[i].point2,l[i].weight);
            add(l[i].point2,l[i].point1,l[i].weight);
        }
        i++;
    }
    int c=0;
    for(i=1;i<=n;i++)
    {
        if(!color[i])
            dfs(i,0,++c,0);
    }
    int num;
    scanf("%d",&num);
    int a,b;
    for(int i=1;i<=num;i++)
    {
        scanf("%d%d",&a,&b);
        if(color[a]!=color[b])//如果不在一个树中
            printf("-1
");
        else
            lca(a,b);
    }
}
/*
输入
5 2
1 2 3
3 4 5
1
3 4
输出
5
*/
原文地址:https://www.cnblogs.com/Lance1ot/p/8521194.html