前言
先把这个题目变好看一点。。。
【
,
】
提高组
货车运输
这道题比
那道题难多了,竟然都是蓝色的。。。
链接
https://www.luogu.org/problemnew/show/P1967
大意
给定一个 个点, 条无向边的图,每条路都有一定的限重,现在有 个货物,求不超过限重的情况下可以带最重的货物
思路
以1为根建造一棵最大生成树(
)
然后再用倍增跑
代码
#include<cstdio>
#include<algorithm>
#define N 20001//n要开两倍
#define M 50001
#define r(i,n) for(int i=1;i<=n;i++)
using namespace std;
struct node{int x,y,z;}v[M];//到时候要用边排序
int n,m,q,fa[N],f[N],p[N],d[N][20],deep[N];//fa是用于Kruskal的并查集数组,f是在倍增中用的并查集数组,我在前面LCA的博客中也有提到
bool cmp(node x,node y){ return x.z>y.z;}//按照载重从大到小排序
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}//并查集
int Lca(int x,int y)//求LCA
{
int t,i=0;
if(deep[x]<deep[y]) {x^=y;y=x^y;x^=y;}//交换x和y
while(i>=0)//这里都是LCA
{
while(deep[d[x][i]]>=deep[y]) x=d[x][i++];
i--;
}
i=0;
while(f[x]!=f[y])//同理
{
while(d[x][i]!=d[y][i]) x=d[x][i],y=d[y][i],i++;
i--;
}
return f[x];//返回
}
int main()
{
scanf("%d%d",&n,&m); int bx,by,tot=0,w=n,k,x,y;
r(i,m) scanf("%d%d%d",&v[i].x,&v[i].y,&v[i].z);
sort(v+1,v+m+1,cmp);//排序
r(i,n) fa[i]=i;//初始化
r(i,m)//以下为Kruskal
{
bx=find(v[i].x);
by=find(v[i].y);
if(bx!=by)
{
fa[by]=fa[bx]=f[bx]=f[by]=++w;
p[w]=v[i].z;
fa[w]=w;
if(++tot==n-1) break;
}
}
n=w;deep[n]=0;
for(int i=n-1;i;i--)
{
deep[i]=deep[f[i]]+1;
d[i][0]=f[i];k=0;
while(d[d[i][k]][k])d[i][k+1]=d[d[i][k]][k],k++;
}//这上面的相当于代替了dfs+倍增,详细可以去看https://blog.csdn.net/xuxiayang/article/details/80369618#t5
scanf("%d",&q);
while(q--)
{
scanf("%d%d",&x,&y);
printf("%d\n",(find(x)==find(y))?p[Lca(x,y)]:-1);//若联通,输出它们到LCA的长度,否则输出-1
}
return 0;
}