[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\n",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\n");
else
lca(a,b);
}
}
/*
输入
5 2
1 2 3
3 4 5
1
3 4
输出
5
*/