https://www.luogu.org/problemnew/show/P1967
题目描述
AA国有n n座城市,编号从 1 1到 nn,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数 n,mn,m,表示 AA 国有 nn 座城市和 mm 条道路。
接下来 mm行每行 3 3个整数 x, y, zx,y,z,每两个整数之间用一个空格隔开,表示从 x x号城市到 y y号城市有一条限重为 zz 的道路。注意: xx 不等于 yy,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
共有 qq 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1−1。
输入输出样例
输入样例#1:
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1:
3
-1
3
说明
对于 30%30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,0000<n<1,000,0<m<10,000,0<q<1,000;
对于 60%60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,0000<n<1,000,0<m<50,000,0<q<1,000;
对于 100%100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,0000<n<10,000,0<m<50,000,0<q<30,000,0≤z≤100,000。
题意:有一个图,q次询问,每次询问两个点u,v,求出从u-v路径边权最小值的最大值
题解:可以知道很多边是永远不会经过的,可按照边权排序之后重构一棵树,然后就是寻找路径上边权的最小值,此时容易想到树链剖分+线段树维护,这是解法一。另一种解法是克鲁斯卡尔重构树。克鲁斯卡尔树就是在边排序之后,重构连边的时候不直接u和v相连,而新建一个节点作为u集合的代表点和v集合的代表点的父节点,并且把这个新节点作为uv集合的代表点,由于是排序,所以父节点一定不小于(或者不大于)子树任意节点,所以求一个路径上的最值就相当于求两个端点的LCA。
树链剖分+线段树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl;
const int maxn=5e4+5;
const int maxn2=1e4+5;
struct edge{
int fr;
int to;
int nex;
int val;
}e[maxn<<1],e2[maxn<<1];
struct node{
int l;
int r;
int minn;
}Node[maxn2<<2];
int cnt,cnt2,head[maxn2],fa[maxn2],id[maxn2],fa2[maxn2],flag[maxn<<1],siz[maxn2],depth[maxn2],acc[maxn2],dfn[maxn2],son[maxn2],top[maxn2],tim;
void adde1(int x,int y,int z){
e[cnt2].fr=x;
e[cnt2].to=y;
e[cnt2].val=z;
cnt2++;
}
void adde(int x,int y,int z){
e2[cnt].fr=x;
e2[cnt].to=y;
e2[cnt].val=z;
e2[cnt].nex=head[x];
head[x]=cnt++;
}
bool cmp(struct edge xx,struct edge yy){
return xx.val>yy.val;
}
void dfs1(int u,int f){
fa[u]=f;
siz[u]=1;
for(int i=head[u];i!=-1;i=e2[i].nex){
int v=e2[i].to;
if(v==f)continue;
depth[v]=depth[u]+1;
acc[v]=e2[i].val;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]){
son[u]=v;
}
}
}
void dfs2(int u,int f,int topp){
top[u]=topp;
dfn[u]=++tim;
id[tim]=u;
if(son[u]){
dfs2(son[u],u,topp);
}
for(int i=head[u];i!=-1;i=e2[i].nex){
int v=e2[i].to;
if(v==f||v==son[u])continue;
dfs2(v,u,v);
}
}
int finds(int x){
int xx=x;
while(fa2[x]!=x){
x=fa2[x];
}
while(fa2[xx]!=x){
int t=fa2[xx];
fa2[xx]=x;
xx=t;
}
return x;
}
void pushup(int rt){
Node[rt].minn=min(Node[rt<<1].minn,Node[(rt<<1)|1].minn);
}
void build(int L,int R,int rt){
Node[rt].l=L;
Node[rt].r=R;
if(L==R){
Node[rt].minn=acc[id[L]];
return;
}
int mid=(L+R)/2;
build(L,mid,rt<<1);
build(mid+1,R,(rt<<1)|1);
pushup(rt);
}
int query(int ll,int rr,int rt,int lll,int rrr){
if(ll>=lll&&rr<=rrr)return Node[rt].minn;
int minx=0x3f3f3f3f;
int mid=(ll+rr)/2;
if(mid>=lll)minx=min(minx,query(ll,mid,rt<<1,lll,rrr));
if(mid<rrr)minx=min(minx,query(mid+1,rr,(rt<<1)|1,lll,rrr));
return minx;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)fa2[i]=i;
for(int i=1;i<=m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
adde1(a,b,c);
}
sort(e,e+cnt2,cmp);
for(int i=0;i<cnt2;i++){
int f1=finds(e[i].fr);
int f2=finds(e[i].to);
if(f1!=f2){
fa2[f1]=f2;
adde(e[i].fr,e[i].to,e[i].val);
adde(e[i].to,e[i].fr,e[i].val);
}
}
for(int i=1;i<=n;i++){
if(!siz[i])dfs1(i,0);
}
for(int i=1;i<=n;i++){
if(!dfn[i])dfs2(i,0,i);
}
build(1,tim,1);
int q;
scanf("%d",&q);
while(q--){
int a,b;
scanf("%d%d",&a,&b);
int minn=0x3f3f3f3f;
int f1=finds(a);
int f2=finds(b);
if(f1!=f2){printf("-1\n");continue;}
while(top[a]!=top[b]){
if(depth[top[a]]<depth[top[b]]){
swap(a,b);
}
if(dfn[top[a]]<=dfn[a])minn=min(query(1,tim,1,dfn[top[a]],dfn[a]),minn);
a=fa[top[a]];
}
if(depth[b]<depth[a])swap(a,b);
if(dfn[a]+1<=dfn[b])minn=min(query(1,tim,1,dfn[a]+1,dfn[b]),minn);
if(minn==0x3f3f3f3f)minn=-1;
printf("%d\n",minn);
}
return 0;
}
克鲁斯卡尔重构树+LCA
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<<x<<endl;
const int maxn=1e4+5;
const int maxn2=5e4+5;
struct edge{
int to;
int nex;
}e[maxn<<2];
struct edge2{
int fr;
int to;
int val;
}e2[maxn2];
int head[maxn<<2],fa[maxn<<2],depth[maxn<<2],acc[maxn<<2],fa2[maxn<<2][31],cnt;
bool cmp(struct edge2 aa,struct edge2 bb){
return aa.val>bb.val;
}
void adde(int x,int y){
e[cnt].to=y;
e[cnt].nex=head[x];
head[x]=cnt++;
}
void dfs(int u,int f){
fa2[u][0]=f;
for(int i=head[u];i!=-1;i=e[i].nex){
int v=e[i].to;
if(v==f)continue;
depth[v]=depth[u]+1;
dfs(v,u);
}
}
int finds(int x){
int xx=x;
while(fa[x]!=x){
x=fa[x];
}
while(fa[xx]!=x){
int t=fa[xx];
fa[xx]=x;
xx=t;
}
return x;
}
int main(){
memset(head,-1,sizeof(head));
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n*2;i++)fa[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&e2[i].fr,&e2[i].to,&e2[i].val);
}
sort(e2+1,e2+1+m,cmp);
int ac=0;
for(int i=1;i<=m;i++){
int f1=finds(e2[i].fr);
int f2=finds(e2[i].to);
if(f1!=f2){
ac++;
adde(f1,n+ac);
adde(n+ac,f1);
adde(f2,n+ac);
adde(n+ac,f2);
acc[n+ac]=e2[i].val;
fa[f1]=n+ac;
fa[f2]=n+ac;
}
}
for(int i=n+ac;i>=1;i--){
if(fa2[i][0]==0)dfs(i,0);
}
for(int i=1;i<31;i++){
for(int j=1;j<=n+ac;j++){
fa2[j][i]=fa2[fa2[j][i-1]][i-1];
}
}
int q;
scanf("%d",&q);
while(q--){
int a,b;
scanf("%d%d",&a,&b);
int f1=finds(a);
int f2=finds(b);
if(f1!=f2){printf("-1\n");continue;}
if(depth[a]<depth[b])swap(a,b);
int acw=depth[a]-depth[b];
for(int i=0;i<31;i++){
if(acw&(1ll<<i)){
a=fa2[a][i];
}
}
for(int i=30;i>=0;i--){
if(fa2[a][i]!=fa2[b][i]){
a=fa2[a][i];
b=fa2[b][i];
}
}
printf("%d\n",acc[fa2[a][0]]);
}
return 0;
}