邦德市

来源:互联网 发布:服务器安装centos系统 编辑:程序博客网 时间:2024/06/05 18:15

邦德市

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计算出ooconst 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;}
原创粉丝点击