Problem H: qwb与学姐
Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 168 Solved: 59
[ Submit][ Status][ Web Board]
Description
qwb打算向学姐表白,可是学姐已经受够了他的骚扰,于是出了一个题想难住他:
已知一幅n个点m条边的无向图,定义路径的值为这条路径上最短的边的长度,
现在有 k个询问,
询问从A点到B点的所有路径的值的最大值。
qwb听完这个问题很绝望啊,聪明的你能帮帮他吗?
已知一幅n个点m条边的无向图,定义路径的值为这条路径上最短的边的长度,
现在有 k个询问,
询问从A点到B点的所有路径的值的最大值。
qwb听完这个问题很绝望啊,聪明的你能帮帮他吗?
Input
一组数据。
第一行三个整数n,m,k (1<=N<=50000,m<=200000,k<=100000)。
第2..m+1行:三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N,1<=D<=2 15 ) 表示X与Y之间有一条长度为D的边。
第m+2..m+k+1行: 每行两个整数A B(1<=A,B<=n且A≠B),意义如题目描述。
保证图连通。
第一行三个整数n,m,k (1<=N<=50000,m<=200000,k<=100000)。
第2..m+1行:三个正整数:X, Y, and D (1 <= X <=N; 1 <= Y <= N,1<=D<=2 15 ) 表示X与Y之间有一条长度为D的边。
第m+2..m+k+1行: 每行两个整数A B(1<=A,B<=n且A≠B),意义如题目描述。
保证图连通。
Output
对于每个询问输出一行,一共k行,每行输出A点到B点的所有路径的值的最大值。
Sample Input
4 5 3
1 2 6
1 3 8
2 3 4
2 4 5
3 4 7
2 3
1 4
3 4
Sample Output
6
7
7
思路:先用克鲁斯卡尔算法求最大生成树,然后求给定两点之间的那条最小的边比赛的时候用dfs实现的但是超时了。然后花了两天搞懂这个倍增法实现lca,再用倍增法实现lca 这里倍增法的详细解释:点击打开链接 http://blog.csdn.net/lw277232240/article/details/72870644
代码:
#include<stdio.h> #include<string.h> #include<vector> #include<stdlib.h> #include<iostream> #include<algorithm> #include<math.h> #define max 50011 #define maxl 25 using namespace std; typedef struct { int from,to,w; }edge;//存储边的结构体 vector<edge>edges;//保存求得最大生成树后的边。 vector<int> G[max];//图 int grand[max][maxl],gw[max][maxl];//倍增表示gw表示x到上面2^i次方的最小值。 int depth[max]; int pre[max]; int n,m,N; edge gc[200010]; bool cmp(edge a,edge b) { return a.w>b.w; } int min(int x,int y) { if(x<y) return x; return y; } void addedge(int x,int y,int w) { edge a={x,y,w},b={y,x,w}; edges.push_back(a); edges.push_back(b); G[x].push_back(edges.size()-2); G[y].push_back(edges.size()-1); } void dfs(int x) { for(int i=1;i<=N;i++) { grand[x][i]=grand[grand[x][i-1]][i-1]; gw[x][i]=min(gw[x][i-1],gw[grand[x][i-1]][i-1]); if(grand[x][i]==0) break; } for(int i=0;i<G[x].size();i++) { edge e = edges[G[x][i]]; if(e.to!=grand[x][0]) { depth[e.to]=depth[x]+1; grand[e.to][0]=x; gw[e.to][0]=e.w; dfs(e.to); } } } int find(int x) { int r=x; while(r!=pre[r]) { r=pre[r]; } int j=x,i; while(j!=r) { i=pre[j]; pre[j]=r; j=i; } return r; } int kruskal() { int top=n; for(int i=1;i<=n;i++) { pre[i]=i; } int sum=0; for(int i=0;i<m;i++) { int f1=find(gc[i].from); int f2=find(gc[i].to); if(f1!=f2) { pre[f2]=f1;top--; addedge(gc[i].from,gc[i].to,gc[i].w); } if(top==1) break; } } void Init(){ //n为节点个数 N = floor(log(n + 0.0) / log(2.0));//最多能跳的2^i祖先 depth[0]=-1; depth[1]=0;//根结点的祖先不存在,用-1表示 memset(grand,0,sizeof(grand)); memset(gw,0,sizeof(gw)); dfs(1);//以1为根节点建树 /* for(int i=1;i<=n;i++) for(int j=0;j<=N;j++) { printf("%d %d min %d\n",i,grand[i][j],gw[i][j]); }*/ } int lca(int a,int b) { if(depth[a] > depth[b]) swap(a, b);//保证a在b上面,便于计算 int min1=999999999; for(int i = N; i >= 0; i--) //类似于二进制拆分,从大到小尝试 if(depth[a] < depth[b] && depth[grand[b][i]] >= depth[a])//a在b下面且b向上跳后不会到a上面 { min1=min(min1,gw[b][i]);b=grand[b][i];}//先把深度较大的b往上跳 for(int j=N;j>=0;j--) { if(grand[a][j]!=grand[b][j]) { min1=min(min1,gw[a][j]); min1=min(min1,gw[b][j]); a=grand[a][j]; b=grand[b][j]; } } if(a!=b) { min1=min(min1,gw[a][0]); min1=min(min1,gw[b][0]); } return min1; } int main() { int t ; scanf("%d%d%d",&n,&m,&t); { for(int i=0;i<m;i++) { int x,y,w; scanf("%d%d%d",&x,&y,&w); edge a={x,y,w}; gc[i]=a; } sort(gc,gc+m,cmp); kruskal(); /* for(int i=0;i<edges.size();i++) { printf("bian %d %d %d\n",edges[i].from,edges[i].to,edges[i].w); }*/ Init(); for(int i=1;i<=t;i++) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } } }