货车运输,既然是noip题,各大oj上肯定都有.
比如说luogu.
题目是这样的:
A国有n座城市,编号从1到n,城市之间有m条双向道路.每一条道路对车辆都有重量限制(限重).现在有q辆货车在运输货物,司机们想知道每辆车在不超过车辆限制的情况下,最多能运多重的货物.
那么这道题暴力30分就可以直接跑spfa来做,不过爆炸很容易.
60分可以先想一想,我们肯定要让每条道上的限重最大,且题目要求是两点之间的路径,每两点之间走的路径肯定是确定的.
那么我们可以发现什么,路径确定了,又要让限重最大,说明我们肯定是直接在一颗最大生成树上跑.
那么我们在最大生成树上暴力跑就有60分了.
100分的思路也是在最大生成树上跑,但是,我们可以想想,我们可以提前预处理每两个点之间的路径.
提前处理路径,那么每两点u和v之间路径其实就是u到lca(u,v)和v到lca(u,v)这两条路径拼起来.
那么lca我们又可以做到O(log(n))的倍增查询,而倍增刚好又可以顺便记录点到它的祖先的路径中边的最小值,这不就可以用O(log(n))的时间完成一次查询了.
注意,若在kruskal过程中u与v不在一个集合内,那么查询他们两个的时候就直接输出-1.
那么这就是正解.
首先先用kruskal求出最大生成树的树边:
int father[N+1]={0}; bool cmp(side_plan a,side_plan b){ return a.v>b.v; } int get(int u){ return (u==father[u])? u:get(father[u]); } void kruskal(){ for (int i=1;i<=n;i++) father[i]=i; sort(e_plan+1,e_plan+m+1,cmp); for (int i=1;i<=m;i++) if (get(e_plan[i].x)!=get(e_plan[i].y)){ father[get(e_plan[i].x)]=get(e_plan[i].y); ins_tree(e_plan[i].x,e_plan[i].y,e_plan[i].v); ins_tree(e_plan[i].y,e_plan[i].x,e_plan[i].v); } }
然后dfs预处理:
int deep[N+1]={0},grand[N+1][20]={0},minn[N+1][20]={0}; bool use[N+1]={0}; void dfs(int k,int dad){ deep[k]=deep[dad]+1; use[k]=1; grand[k][0]=dad; for (int i=1;grand[k][i-1];i++) grand[k][i]=grand[grand[k][i-1]][i-1],minn[k][i]=min(minn[k][i-1],minn[grand[k][i-1]][i-1]); for (int i=lin_tree[k];i;i=e_tree[i].next) if (!use[e_tree[i].y]) minn[e_tree[i].y][0]=e_tree[i].v,dfs(e_tree[i].y,k); }
整个查询可以这样写:
int query(int x,int y){ int mi=INF; if (deep[x]<deep[y]) swap(x,y); for (int i=19;i>=0;i--) if (deep[grand[x][i]]>=deep[y]) mi=min(mi,minn[x][i]),x=grand[x][i]; for (int i=19;i>=0;i--) if (grand[x][i]!=grand[y][i]) mi=min(mi,min(minn[x][i],minn[y][i])),x=grand[x][i],y=grand[y][i]; if (x!=y) return min(mi,min(minn[x][0],minn[y][0])); else return mi; } inline void outo(){ int x,y,q; for (int i=1;i<=n;i++) for (int j=0;grand[i][j];j++); scanf("%d",&q); for (int i=1;i<=q;i++){ scanf("%d%d",&x,&y); if (get(x)!=get(y)) printf("-1\n"); else printf("%d\n",query(x,y)); } }
AC代码如下:
#include<bits/stdc++.h> using namespace std; const int M=50000; const int N=10000; const int INF=100000000; int n,m; struct side_plan{ int x,y,v; }e_plan[M+1]; int top_plan=0; void ins_plan(int X,int Y,int V){ e_plan[++top_plan].x=X; e_plan[top_plan].y=Y; e_plan[top_plan].v=V; } struct side_tree{ int y,next,v; }e_tree[2*N+1]; int top_tree=0,lin_tree[N+1]; void ins_tree(int X,int Y,int V){ e_tree[++top_tree].y=Y; e_tree[top_tree].v=V; e_tree[top_tree].next=lin_tree[X]; lin_tree[X]=top_tree; } inline void into(){ memset(e_plan,0,sizeof(e_plan)); memset(e_tree,0,sizeof(e_tree)); scanf("%d%d",&n,&m); int x,y,v; for (int i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&v); ins_plan(x,y,v); } } int father[N+1]={0}; bool cmp(side_plan a,side_plan b){ return a.v>b.v; } int get(int u){ return (u==father[u])? u:get(father[u]); } void kruskal(){ for (int i=1;i<=n;i++) father[i]=i; sort(e_plan+1,e_plan+m+1,cmp); for (int i=1;i<=m;i++) if (get(e_plan[i].x)!=get(e_plan[i].y)){ father[get(e_plan[i].x)]=get(e_plan[i].y); ins_tree(e_plan[i].x,e_plan[i].y,e_plan[i].v); ins_tree(e_plan[i].y,e_plan[i].x,e_plan[i].v); } } int deep[N+1]={0},grand[N+1][20]={0},minn[N+1][20]={0}; bool use[N+1]={0}; void dfs(int k,int dad){ deep[k]=deep[dad]+1; use[k]=1; grand[k][0]=dad; for (int i=1;grand[k][i-1];i++) grand[k][i]=grand[grand[k][i-1]][i-1],minn[k][i]=min(minn[k][i-1],minn[grand[k][i-1]][i-1]); for (int i=lin_tree[k];i;i=e_tree[i].next) if (!use[e_tree[i].y]) minn[e_tree[i].y][0]=e_tree[i].v,dfs(e_tree[i].y,k); } inline void work(){ kruskal(); memset(minn,10,sizeof(minn)); for (int i=1;i<=n;i++) if (!use[i]) dfs(i,0); } int query(int x,int y){ int mi=INF; if (deep[x]<deep[y]) swap(x,y); for (int i=19;i>=0;i--) if (deep[grand[x][i]]>=deep[y]) mi=min(mi,minn[x][i]),x=grand[x][i]; for (int i=19;i>=0;i--) if (grand[x][i]!=grand[y][i]) mi=min(mi,min(minn[x][i],minn[y][i])),x=grand[x][i],y=grand[y][i]; if (x!=y) return min(mi,min(minn[x][0],minn[y][0])); else return mi; } inline void outo(){ int x,y,q; for (int i=1;i<=n;i++) for (int j=0;grand[i][j];j++); scanf("%d",&q); for (int i=1;i<=q;i++){ scanf("%d%d",&x,&y); if (get(x)!=get(y)) printf("-1\n"); else printf("%d\n",query(x,y)); } } int main(){ into(); work(); outo(); return 0; }