题目
题目描述
A国有n座城市,编号从1到n,城市之间有m条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数n,m表示A国有n座城市和m条道路。
接下来m行每行3个整数 x, y, z每两个整数之间用一个空格隔开,表示从x号城市到y号城市有一条限重为z的道路。注意:x不等于y,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
共有q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出−1。
输入输出样例
输入样例1:
1 2 3 4 5 6 7 8 9 |
4 3 1 2 4 2 3 3 3 1 1 3 1 3 1 4 1 3 |
输出样例1:
1 2 3 4 |
3 -1 3 |
说明
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000
题解
部分内容摘自2013年鉴
其实这道题就是比较简单的图论题(作为最后一天的最后一题来讲)
首先对图做最大生成树(方法和最小生成树一样),得到的是一个森林。对于每一辆车,设其起点、终点分别为x,y,如果x,y连通,那么x,y在一棵树上,该火车最多能运送的货物为x,y之间的路径中的最小的边c。因为如果在原图中存在一条路径连接x,y,且所有的边的边权都大于c,那么最大生成树一定求错了(因为将该路径上的边加到生成树中去,必然形成包括c边在内的一个环,此时删除c边可以得到更大的生成树)
所以此题变成了求各个点对在树种路径上的最小边。
方法一:暴力
就是去实现上面的步骤,求LCA的时候暴力来求,不用倍增,可以AC(具体见暴力),如果是一条链的话会被卡,然而并没有这种数据。
方法二:倍增
此时我们可以用倍增的子项求某两个点的路径上的最小边。首先纪录每个点向上(向树根)走\(v(v=2^0,2^1,\dots)\)步所经过的路径中最小边是多少。对于一个询问x,y,可以求出其最近公共祖先z,然后求x,z之间的最小边和y,z之间的最小边。
时间复杂度为\(O(mlogm+n+logn)\)
方法三:
标称用的方法并不是倍增法,只需要在最大生成树的里面加一个求解的询问就行。首先用一个数组ans表示最终的答案,ans[i]表示第i个询问最多能运的货物数量。再用kruskal算法中,每将一条边加到最大生成树里面,那么这条边会将连个点集合并到一起。
如果某个询问i,他的两个点x,y分别属于这两个点集,那么这个询问的答案就是新加入的这条边的边权(因为此时x,y已经在一棵树中,所以x,y之间有路径。由于Kruskal求最大生成树时是由边权从大到小加入的,所以当前边是路径上边权最小的边)我们只需要在每个电商挂一个询问的链,每次询问时将两个询问的链合并到一起,合并的时候选取询问较少的那个链进行扫描并更新答案。所以对链的操作的时间复杂度为[合并次数+每次合并时扫描的询问个数和]。时间的小号主要在扫描,但是我们可以发现,每次扫描都只扫描了较少的那一部分,设长度为L,并且合并后询问的长度增长到2L以上。所以总的时间复杂度为\(O(mlogm+qlogq)\)
代码
LCA暴力,代码来自本校神犇yanyu
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn=11000; const int maxm=51000; int head[maxn],Next[maxm<<1],ver[maxm<<1],weigh[maxm<<1]; int f[maxn],fa[maxn],dis[maxn],deep[maxn]; int n,m,q,tot; struct node{ int x,y,weigh; }edge[maxm<<1]; inline void add(int x,int y,int w){ ver[++tot]=y;weigh[tot]=w;Next[tot]=head[x];head[x]=tot; } bool cmp(node a,node b){ return a.weigh > b.weigh; } int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } void dfs(int x){ for(int i=head[x];i;i=Next[i]){ int y=ver[i]; if(deep[y]==0){ dis[y]=weigh[i]; deep[y]=deep[x]+1; f[y]=x; dfs(y); } } } int lca(int x,int y){ int ans=1<<30; if(deep[x]>deep[y]) swap(x,y); while(deep[x]!=deep[y]){ ans=min(ans,dis[y]); y=f[y]; } while(x!=y){ ans=min(ans,min(dis[x],dis[y])); x=f[x],y=f[y]; } return ans; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].weigh); sort(edge+1,edge+m+1,cmp); for(int i=1;i<=m;i++){ int fx=find(edge[i].x); int fy=find(edge[i].y); if(fx!=fy){ fa[fx]=fy; add(edge[i].x,edge[i].y,edge[i].weigh); add(edge[i].y,edge[i].x,edge[i].weigh); } } for(int i=1;i<=n;i++) if(deep[i]==0) dfs(i); scanf("%d",&q); for(int i=1,x,y;i<=q;i++){ scanf("%d%d",&x,&y); if(find(x)!=find(y)) printf("-1\n"); else printf("%d\n",lca(x,y)); } return 0; } |
倍增
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int MAXN =50005; int dep[MAXN]; int n,m; struct node{ int x,y,w; }edge[MAXN]; int fa[MAXN]; int Nt[MAXN],Head[MAXN],wight[MAXN],to[MAXN],tot; int f[20005][21],w[20005][21]; void add(int x,int y,int v){ Nt[++tot]=Head[x]; to[tot]=y; wight[tot]=v; Head[x]=tot; } int Find(int x){ if(fa[x]!=x)fa[x]=Find(fa[x]); return fa[x]; } bool cmp(node a,node b){ return a.w>b.w; } void dfs(int x){ for(int i=Head[x];i;i=Nt[i]){ int y=to[i]; if(dep[y])continue; dep[y]=dep[x]+1; f[y][0]=x; w[y][0]=wight[i]; dfs(y); } } void kruskal(){ sort(edge+1,edge+m+1,cmp); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1;i<=m;i++){ int fa_x=Find(edge[i].x),fa_y=Find(edge[i].y); if(fa_x==fa_y)continue; fa[fa_x]=fa_y; add(edge[i].x,edge[i].y,edge[i].w); add(edge[i].y,edge[i].x,edge[i].w); } } int lca(int x,int y){ if(Find(x)!=Find(y)) return -1; int ans=1<<30; if(dep[x]>dep[y])swap(x,y); for(int i=20;i>=0;i--){ if(dep[f[y][i]]>=dep[x]){ ans=min(ans,w[y][i]); y=f[y][i]; } } if(x==y)return ans; for(int i=20;i>=0;i--){ if(f[x][i]!=f[y][i]){ ans=min(ans,min(w[x][i],w[y][i])); y=f[y][i]; x=f[x][i]; } } ans=min(ans, min(w[x][0], w[y][0])); return ans; } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w); } kruskal(); int q; cin>>q; for(int i=1;i<=n;i++){ if(!dep[i]){ dep[i]=1; dfs(i); f[i][0]=i; w[i][0]=1<<30; } } for(int i=1;i<=20;i++){ for(int j=1;j<=n;j++){ f[j][i]=f[f[j][i-1]][i-1]; w[j][i]=min(w[j][i-1],w[f[j][i-1]][i-1]); } } for(int i=1;i<=q;i++){ int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; } |