邦德市
1699(最优生成树(并查集)、LCA(倍增))
邦德市有N(编号为1..N)个城市,用M条双向道路连接,每条道路都有一个危险系数。你的任务是回答若干询问,每个询问包含一个起点s和一个终点t,要求找到一条s到t的路径,使得路径上所有边的最大危险系数最小。
第一行一个包含两个整数N和M;接下来的M行,每行包含三个整数:x,y,d(1<=x,y<=N,0<=d<=1 000 000 000),即城市x于城市y有一条危险系数为d的双向道路。下一行一个整数Q,表示有Q个询问;接下来的Q行,每行包含两个整数s,t(s!=t),表示一个询问的起点和终点。
对于每个询问,输出最优路线上所有边的危险系数的最大值。(输入数据保证所有城市是连通的)。
分析
这是一道综合性比较强的题目,反正有些知识点也忘了,干脆就直接顺带回忆
原图中每个点所关联的最小边一定是最小生成树中的边。
下面的行文风格并不是我的风格,是因为我突然抽风像这么写,但是发现并不好写
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
#define oo 13//通过m计算出oo
const int maxn=10005,maxm=100005;
struct edge{
int u,v,w;//最优生成树的储存结构是用的这种形式
friend bool operator<(edge a,edge b){
return a.w<b.w;
}
}E[maxm];
int n,m,q,np;
int pa[maxn];
int fa[maxn][20],dep[maxn],dist[maxn][20];//倍增
vector<int>gc[maxn],gw[maxn];//采取相同位置的技巧可以将j和w存在两个数组里面,因为他们位置不会改变,就用不着结构体
void initial(int x){
for(int i=1;i<=n;i++)
pa[i]=i;
}
int find(int x){
if(pa[x]==x)
return x;
int root=find(pa[x]);
return pa[x]=root;
}
bool judge(int x,int y){
return find(x)==find(y);
}
void merge(int x,int y){
int px=find(x),py=find(y);
pa[px]=py;
}
void Init()
{
int u,v,w;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
E[i]=(edge){u,v,w};//只用存一边即可
}
sort(E+1,E+m+1);
}
void shengcheng(){
initial(n);
int cnt=0;
for(int i=1;i<=m;i++){
int x=E[i].u,y=E[i].v,w=E[i].w;
if(judge(x,y))continue;
cnt++;
merge(x,y);
gc[x].push_back(y);
gc[y].push_back(x);
gw[x].push_back(w);
gw[y].push_back(w);//储存技巧
if(cnt==n-1)
break;
/*关于这个break写还是不写的问题,其实并没有什么用,时间是差不多的,
因为排序都已经是mlogm,这点算不了什么,而且cnt++本身也是需要计算时间的,
只有当m远大于n或者需要判断是否生成了树再加吧*/
}
}
void DFS(int i,int f,int depp,int w){
fa[i][0]=f;
dep[i]=depp;
dist[i][0]=w;
for(int j=1;j<=oo;j++){
fa[i][j]=fa[fa[i][j-1]][j-1];//倍增顺序,现有fa才有dist
dist[i][j]=max(dist[i][j-1],dist[fa[i][j-1]][j-1]);
}
for(int p=0;p<gc[i].size();p++){
int j=gc[i][p],w=gw[i][p];
if(j==f)continue;
DFS(j,i,depp+1,w);
}
}
int LCA(int u1,int u2){//很有技巧性
int ret=0;
if(dep[u1]<dep[u2])
swap(u1,u2);
int d=dep[u1]-dep[u2];
for(int j=oo;j>=0;j--){//j的顺序不能反,下面也是
if((1<<j)&d){//&这个符号不要搞忘了
ret=max(ret,dist[u1][j]);//要先改值再倍增,和生成倍增数组是不同
u1=fa[u1][j];
}
}
if(u1==u2)
return ret;//这里要改成ret
for(int j=oo;j>=0;j--){
if(fa[u1][j]!=fa[u2][j]){
ret=max(ret,dist[u1][j]);
ret=max(ret,dist[u2][j]);
u1=fa[u1][j];
u2=fa[u2][j];
}
}
return max(ret,max(dist[u1][0],dist[u2][0]));//最后还要判断一次父节点
}//感觉就是回忆了一次LCA呢
void out(){
for(int i=1;i<=n;i++){
cout<<i<<":";
for(int p=0;p<gc[i].size();p++){
int j=gc[i][p];
cout<<j<<" "<<gw[i][p]<<" ";
}
cout<<endl;
}
}
void query(){
scanf("%d",&q);
int u1,u2;
while(q--){
scanf("%d%d",&u1,&u2);
LCA(u1,u2);
printf("%d\n",LCA(u1,u2));
}
}
int main()
{
//freopen("in.txt","r",stdin);
Init();
shengcheng();
DFS(1,0,1,0);
query();
return 0;
}

805

被折叠的 条评论
为什么被折叠?



