qwb与学姐---之江学院第0届校赛最大生成树+lca

Problem H: qwb与学姐

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 168   Solved: 59
[ Submit][ Status][ Web Board]

Description

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),意义如题目描述。
保证图连通。

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));
     }
  }
}


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值