洛谷P1967货车运输
Posted 那一抹落日的橙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P1967货车运输相关的知识,希望对你有一定的参考价值。
这个题要求货车从a到b最大能运多少货物(不能输出-1),那么自然而然的就可以想到最大生成树,这个很好求,重点在于如何快速的查找树上两点间的最大边权,这个时候我们可以运用倍增来解决,因为这两个点都在树上,显然联通它们的路径上有些边是一定要走的,这些边就是它们到最近公共祖先的边,那么答案就在这些边当中,那么我们可以运用lca的办法,设mx[i][j]表示i到它的2^j号父亲的路上边权的最小值是多少
这个题难点就在于细节上,要注意提前把所有点的深度初始化为1
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int n,m,q,a,b,tail,tot,head[10010],mx[10010][20],fa[10010],f[10010][20],de[10010]; bool flag[10010]; struct in { int x,y,z; }ter[50050]; struct es { int to,ne,co; }ing[20020]; in ex[10010]; bool cmp(in a,in b) { return a.z>b.z; } int find(int x) { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } inline void kru() { for(int i=1;i<=n;i++) fa[i]=i;//初始化所有的点的父亲,均为它们自己 tail=1,tot=0; while(tail<=m&&tot<n-1) { int owo=find(ter[tail].x),wow=find(ter[tail].y); if(owo!=wow)//合并 { fa[owo]=wow; ex[++tot]=ter[tail]; } tail++; } } inline void build(int f,int l,int c)//双向建图 { ing[++tail]=(es){l,head[f],c},head[f]=tail; ing[++tail]=(es){f,head[l],c},head[l]=tail; } void dfs(int x)//提前预处理深度,每个点2^0的父亲及其答案 { flag[x]=1; for(int i=head[x];i!=-1;i=ing[i].ne) { int t=ing[i].to; if(flag[t]) continue; f[t][0]=x,mx[t][0]=ing[i].co,de[t]=de[x]+1; dfs(t); } } int lca(int a,int b) { if(de[a]<de[b])//保证a在下面,便于求lca swap(a,b); for(int i=log2(n);i>=0;i--) { if(de[f[a][i]]>=de[b]) a=f[a][i]; } if(a==b)//如果a b在a经过跳转后都在同一个点上,说明答案为a return a; for(int i=log2(n);i>=0;i--) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i]; return f[a][0];//不停跳,直到两点上方为最近公共祖先 } int ask(int a,int b) { int re=1000000007; for(int i=log2(n);i>=0;i--)//模仿求lca if(de[f[a][i]]>=de[b]) re=min(re,mx[a][i]),a=f[a][i]; return re; } inline void make_lca()//提前预处理好i的2^j的父亲及其答案 { for(int i=1;i<=log2(n);i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1],mx[j][i]=min(mx[f[j][i-1]][i-1],mx[j][i-1]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&ter[i].x,&ter[i].y,&ter[i].z); sort(ter+1,ter+1+m,cmp); kru();//先求出能够构建最大生成树(森林)的边 memset(head,-1,sizeof(head)),tail=0; memset(de,1,sizeof(de));//防止它跑到0这个不存在的点上 de[0]=0; for(int i=1;i<=n-1;i++) build(ex[i].x,ex[i].y,ex[i].z);//建图 for(int i=1;i<=n;i++) if(!flag[i])//这样做是因为不排除森林的可能性 dfs(i); make_lca(); scanf("%d",&q); for(int i=1;i<=q;i++) { scanf("%d%d",&a,&b); if(find(a)!=find(b))//如果二者不在一棵树上,直接不可能到达 { printf("-1\n");continue; } else { int t=lca(a,b);//先求最近公共祖先 printf("%d\n",min(ask(a,t),ask(b,t)));//输出各自到公共祖先的路上的边的最小值 } } }
#include<algorithm>#include<iostream> #include<cstring>#include<cstdio>#include<cmath>using namespace std;int n,m,q,a,b,tail,tot,head[10010],mx[10010][20],fa[10010],f[10010][20],de[10010];bool flag[10010]; struct in{int x,y,z;}ter[50050];struct es{int to,ne,co;}ing[20020];in ex[10010];bool cmp(in a,in b){return a.z>b.z;}int find(int x){if(fa[x]==x)return x;return fa[x]=find(fa[x]);}inline void kru(){for(int i=1;i<=n;i++)fa[i]=i;//初始化所有的点的父亲,均为它们自己 tail=1,tot=0;while(tail<=m&&tot<n-1){int owo=find(ter[tail].x),wow=find(ter[tail].y);if(owo!=wow)//合并 {fa[owo]=wow;ex[++tot]=ter[tail];}tail++;}}inline void build(int f,int l,int c)//双向建图 {ing[++tail]=(es){l,head[f],c},head[f]=tail;ing[++tail]=(es){f,head[l],c},head[l]=tail;}void dfs(int x)//提前预处理深度,每个点2^0的父亲及其答案 {flag[x]=1;for(int i=head[x];i!=-1;i=ing[i].ne){int t=ing[i].to;if(flag[t])continue;f[t][0]=x,mx[t][0]=ing[i].co,de[t]=de[x]+1;dfs(t); }}int lca(int a,int b){if(de[a]<de[b])//保证a在下面,便于求lca swap(a,b);for(int i=log2(n);i>=0;i--){if(de[f[a][i]]>=de[b])a=f[a][i];}if(a==b)//如果a b在a经过跳转后都在同一个点上,说明答案为a return a;for(int i=log2(n);i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];return f[a][0];//不停跳,直到两点上方为最近公共祖先 }int ask(int a,int b){int re=1000000007;for(int i=log2(n);i>=0;i--)//模仿求lcaif(de[f[a][i]]>=de[b])re=min(re,mx[a][i]),a=f[a][i];return re;}inline void make_lca()//提前预处理好i的2^j的父亲及其答案 {for(int i=1;i<=log2(n);i++)for(int j=1;j<=n;j++)f[j][i]=f[f[j][i-1]][i-1],mx[j][i]=min(mx[f[j][i-1]][i-1],mx[j][i-1]);} int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++)scanf("%d%d%d",&ter[i].x,&ter[i].y,&ter[i].z);sort(ter+1,ter+1+m,cmp);kru();//先求出能够构建最大生成树(森林)的边 memset(head,-1,sizeof(head)),tail=0;memset(de,1,sizeof(de));//防止它跑到0这个不存在的点上 de[0]=0;for(int i=1;i<=n-1;i++)build(ex[i].x,ex[i].y,ex[i].z);//建图 for(int i=1;i<=n;i++)if(!flag[i])//这样做是因为不排除森林的可能性 dfs(i);make_lca();scanf("%d",&q);for(int i=1;i<=q;i++){scanf("%d%d",&a,&b);if(find(a)!=find(b))//如果二者不在一棵树上,直接不可能到达 {printf("-1\n");continue;}else{int t=lca(a,b);//先求最近公共祖先 printf("%d\n",min(ask(a,t),ask(b,t)));//输出各自到公共祖先的路上的边的最小值 }}}
以上是关于洛谷P1967货车运输的主要内容,如果未能解决你的问题,请参考以下文章