【洛谷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(intx) { 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】貨車運輸